6、登录功能后端开发
https://xiaoxueblog.com/ai/%E7%99%BB%E5%BD%95%E5%8A%9F%E8%83%BD%E5%90%8E%E7%AB%AF%E5%BC%80%E5%8F%91.html
1、新建用户表SQL脚本
drop table if exists t_user;
CREATE TABLE `t_user`
(
id bigint not null comment '主键 ID',
`username` VARCHAR(50) NOT NULL DEFAULT '' COMMENT '用户名',
`password` VARCHAR(200) NOT NULL DEFAULT '' COMMENT '密码',
`photo` VARCHAR(20) DEFAULT NULL COMMENT '头像',
`phone` VARCHAR(20) DEFAULT NULL COMMENT '手机',
`status` INT NOT NULL DEFAULT '1' COMMENT '状态 1、启用 2、禁用',
`use_count` INT NOT NULL DEFAULT '0' COMMENT '每天可以使用大模型的次数',
create_id bigint DEFAULT null comment '创建人ID',
create_by varchar(30) DEFAULT null comment '创建人',
create_time datetime DEFAULT null comment '创建时间',
update_by varchar(30) DEFAULT null comment '修改人',
update_time datetime DEFAULT null comment '修改时间',
deleted smallint default 0 not null comment '删除 0、否 1、是',
PRIMARY KEY (`id`)
) ENGINE = INNODB
DEFAULT CHARSET = utf8mb4 COMMENT ='用户表';
INSERT INTO t_user (id, username, password, photo, phone, status, use_count, create_id, create_by, create_time, update_by, update_time, deleted) VALUES (1, 'admin', '123456', null, null, 1, 0, null, null, null, null, null, 0);
2、BaseEntity
package com.xx.entities;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Getter;
import lombok.Setter;
import java.util.Date;
@Setter
@Getter
public class BaseEntity {
@TableField(fill = FieldFill.INSERT)
protected Long createId;
@TableField(fill = FieldFill.INSERT)
protected String createBy;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@TableField(fill = FieldFill.INSERT)
protected Date createTime;
@TableField(fill = FieldFill.UPDATE)
protected String updateBy;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@TableField(fill = FieldFill.UPDATE)
protected Date updateTime;
@JsonIgnore
@TableLogic(value = "0", delval = "1")
private Integer deleted;
}
3、User
package com.xx.entities;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
@TableName(value = "t_user")
@Data
public class User extends BaseEntity implements Serializable {
@TableId
@Schema(description = "")
private Long id;
@Schema(description = "用户名")
private String username;
@Schema(description = "密码")
private String password;
@Schema(description = "头像")
private String photo;
@Schema(description = "手机")
private String phone;
@Schema(description = "状态 1、启用 2、禁用")
private Integer status;
@Schema(description = "每天可以使用大模型的次数")
private Integer useCount;
}
package com.xx.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
@Data
public class LoginDTO implements Serializable {
@Schema(description = "用户名")
private String username;
@Schema(description = "密码")
private String password;
}
package com.xx.vo;
import com.baomidou.mybatisplus.annotation.TableId;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
@Data
public class LoginVO implements Serializable {
@TableId
@Schema(description = "")
private Long id;
@Schema(description = "用户名")
private String username;
@Schema(description = "密码")
private String password;
@Schema(description = "头像")
private String photo;
@Schema(description = "手机")
private String phone;
@Schema(description = "状态 1、启用 2、禁用")
private Integer status;
@Schema(description = "每天可以使用大模型的次数")
private Integer useCount;
}
4、UserMapper
package com.xx.mapper;
import com.xx.entities.User;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
public interface UserMapper extends BaseMapper<User> {
}
5、UserService
package com.xx.service;
import com.xx.dto.LoginDTO;
import com.xx.entities.User;
import com.baomidou.mybatisplus.extension.service.IService;
import com.xx.vo.LoginVO;
public interface UserService extends IService<User> {
LoginVO login(LoginDTO loginDTO);
}
package com.xx.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xx.dto.LoginDTO;
import com.xx.exception.BusinessException;
import com.xx.service.UserService;
import com.xx.entities.User;
import com.xx.mapper.UserMapper;
import com.xx.utils.ValidationUtil;
import com.xx.utils.ValueUtil;
import com.xx.vo.LoginVO;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
implements UserService {
@Override
public LoginVO login(LoginDTO loginDTO) {
if(ValidationUtil.isEmpty(loginDTO)){
throw new BusinessException("请求参数不能为空");
}
if(ValidationUtil.isEmpty(loginDTO.getUsername()) || ValidationUtil.isEmpty((loginDTO.getPassword()))){
throw new BusinessException("用户名或者密码不能为空");
}
LambdaQueryWrapper<User> queryWrapper = Wrappers.lambdaQuery();
queryWrapper.eq(User::getUsername,loginDTO.getUsername());
User user = getOne(queryWrapper);
if(ValidationUtil.isEmpty(user)){
throw new BusinessException("用户名或者密码错误");
}
if(!user.getPassword().equals(loginDTO.getPassword())){
throw new BusinessException("用户名或者密码错误");
}
return ValueUtil.copyFieldValue(user,LoginVO.class);
}
}
6、UserController
package com.xx.controller;
import com.xx.common.Result;
import com.xx.dto.LoginDTO;
import com.xx.service.UserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
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("/user")
@Tag(name = "用户管理")
public class UserController {
@Resource
private UserService userService;
@Operation(summary = "用户登录")
@PostMapping(value = "/login")
public Result login(@RequestBody LoginDTO loginDTO) {
return Result.ok(userService.login(loginDTO));
}
}
7、MybatisPlusConfig
package com.xx.config;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@MapperScan(value = {"com.xx.mapper"})
public class MybatisPlusConfig {
}
8、全局异常
package com.xx.config;
import com.xx.common.Result;
import com.xx.exception.BusinessException;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@Slf4j
@RestControllerAdvice
public class GlobalExcHandler {
@ExceptionHandler(BusinessException.class)
@ResponseStatus(HttpStatus.OK)
@ResponseBody
public Result<String> businessException(BusinessException e, HttpServletRequest request) {
log.error("系统异常", e);
return Result.error(e.getMessage());
}
}
9、application.yml
server:
port: 8001
spring:
application:
name: xx-ai-cloud
ai:
openai:
api-key:
base-url: https://gitaigc.com
chat:
options:
model: gpt-3.5-turbo
image:
options:
model: gpt-4-dalle
---
spring:
data:
redis:
host: 127.0.0.1
password: 123456
port: 6379
timeout: 1s
---
spring:
sql:
init:
mode: always
schema-locations: classpath:db/init.sql
---
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/aicloud?characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true
username: root
password: mac_root
druid:
test-while-idle: false
mybatis-plus:
configuration:
map-underscore-to-camel-case: true
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.xx.entities
logging:
level:
com.xx: info
---
spring:
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
10、登录页面
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Demo</title>
<link href="/layui/css/layui.css" rel="stylesheet">
</head>
<body>
<style>
.demo-login-container{width: 320px; margin: 21px auto 0;}
.demo-login-other .layui-icon{position: relative; display: inline-block; margin: 0 2px; top: 2px; font-size: 26px;}
</style>
<form class="layui-form">
<div class="demo-login-container">
<div class="layui-form-item">
<div class="layui-input-wrap">
<div class="layui-input-prefix">
<i class="layui-icon layui-icon-username"></i>
</div>
<input type="text" name="username" value="" lay-verify="required" placeholder="用户名" lay-reqtext="请填写用户名" autocomplete="off" class="layui-input" lay-affix="clear">
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-wrap">
<div class="layui-input-prefix">
<i class="layui-icon layui-icon-password"></i>
</div>
<input type="password" name="password" value="" lay-verify="required" placeholder="密 码" lay-reqtext="请填写密码" autocomplete="off" class="layui-input" lay-affix="eye">
</div>
</div>
<div class="layui-form-item">
<div class="layui-row">
<div class="layui-col-xs7">
<div class="layui-input-wrap">
<div class="layui-input-prefix">
<i class="layui-icon layui-icon-vercode"></i>
</div>
<input type="text" name="captcha" value="" lay-verify="required" placeholder="验证码" lay-reqtext="请填写验证码" autocomplete="off" class="layui-input" lay-affix="clear">
</div>
</div>
<div class="layui-col-xs5">
<div style="margin-left: 10px;">
<img src="https://www.oschina.net/action/user/captcha" onclick="this.src='https://www.oschina.net/action/user/captcha?t='+ new Date().getTime();">
</div>
</div>
</div>
</div>
<div class="layui-form-item">
<input type="checkbox" name="remember" lay-skin="primary" title="记住密码">
<a href="#forget" style="float: right; margin-top: 7px;">忘记密码?</a>
</div>
<div class="layui-form-item">
<button class="layui-btn layui-btn-fluid" lay-submit lay-filter="demo-login">登录</button>
</div>
<div class="layui-form-item demo-login-other">
<label>社交账号登录</label>
<span style="padding: 0 21px 0 6px;">
<a href="javascript:;"><i class="layui-icon layui-icon-login-qq" style="color: #3492ed;"></i></a>
<a href="javascript:;"><i class="layui-icon layui-icon-login-wechat" style="color: #4daf29;"></i></a>
<a href="javascript:;"><i class="layui-icon layui-icon-login-weibo" style="color: #cf1900;"></i></a>
</span>
或 <a href="#reg">注册帐号</a>
</div>
</div>
</form>
<script src="/layui/layui.js"></script>
<script>
layui.use(function(){
var form = layui.form;
var layer = layui.layer;
var jQuery = layui.$;
form.on('submit(demo-login)', function (data) {
var field = data.field;
jQuery.ajax({
url: '/user/login',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify(field),
success: function (result) {
if (result.code == 200) {
layer.msg("登录成功: "+result.result.username);
} else {
layer.msg("登录失败: "+result.code+"\t"+result.message);
}
}
});
return false;
});
});
</script>
</body>
</html>