6、登录功能后端开发

发布于:2025-05-16 ⋅ 阅读:(16) ⋅ 点赞:(0)

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脚本

-- CREATE DATABASE aicloud CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

-- 创建用户表
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);
--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', '$2a$10$UceAABzbebzO5929OXo6auLgqaCOlfbQc6q.G4YqoVcerWPsGuIZa', 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;

/**
 * @Author: xueqimiao
 * @Date: 2024/11/13 15:55
 */
@Setter
@Getter
public class BaseEntity {


    /**
     * 创建人ID
     */
    @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;

    /**
     * 删除 0、否 1、是
     */
    @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 t_user
 */
@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;

/**
 * @Author: xueqimiao
 * @Date: 2025/3/5 14:34
 */
@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;

/**
 * @Author: xueqimiao
 * @Date: 2025/3/5 14:38
 */
@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;

/**
 * @author
 * @description 针对表【t_user(用户表)】的数据库操作Mapper
 * @createDate 2024-10-05 16:13:06
 * @Entity com.entities.User
 */
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;

/**
 * @author
 * @description 针对表【t_user(用户表)】的数据库操作Service
 * @createDate 2024-10-05 16:13:06
 */
public interface UserService extends IService<User> {

    /**
     * 登录
     * @param loginDTO
     * @return
     */
    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;

/**
 * @author
 * @description 针对表【t_user(用户表)】的数据库操作Service实现
 * @createDate 2024-10-05 16:13:06
 */
@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;

/**
 * @auther xx
 * @create 2024-10-03 19:48
 */
@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;

/**
 * @Author: xueqimiao
 * @Date: 2025/3/5 14:48
 */
@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:   # OpenAI API 密钥
      base-url: https://gitaigc.com  # OpenAI 基础 URL
      chat:
        options:
          model: gpt-3.5-turbo
      image:
        options:
          model: gpt-4-dalle
#      base-url: https://api.openai.com  # OpenAI 基础 URL

      # 以下是可选的配置,已经被注释掉
      # spring.ai.openai.base-url: https://api.openai.com
      # spring.ai.openai.api-key:

      # 生成文本(text-to-text)模型配置
      # spring.ai.openai.chat.options.model: gpt-3.5-turbo
      # spring.ai.openai.chat.options.temperature: 0.4

      # 生成图像(text-to-image)模型配置
#      spring.ai.openai.image.options.model: gpt-4-dalle
#      spring.ai.openai.image.options.model: gpt-3.5-turbo


# ========================Redis 配置=====================
---
spring:
  data:
    redis:
      host: 127.0.0.1  # Redis 主机
      password: 123456  # Redis 密码
      port: 6379  # Redis 端口
      timeout: 1s  # Redis 连接超时

# ========================SQL 初始化配置===================
---
spring:
  sql:
    init:
      mode: always  # 总是初始化数据库
      schema-locations: classpath:db/init.sql  # 初始化SQL文件位置

# ========================数据库配置(Druid + MySQL 8)=======================
---
spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource  # 数据源类型
    driver-class-name: com.mysql.cj.jdbc.Driver  # MySQL JDBC 驱动类名
    url: jdbc:mysql://127.0.0.1:3306/aicloud?characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true  # 数据库连接 URL
    username: root  # 数据库用户名
    password: mac_root  # 数据库密码
    druid:
      test-while-idle: false  # 配置 Druid 数据源的空闲连接检测

# ========================MyBatis-Plus 配置===================
mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true  # 将数据库表字段的下划线转为驼峰命名法
  mapper-locations: classpath:mapper/*.xml  # MyBatis 映射器文件的位置
  type-aliases-package: com.xx.entities  # MyBatis 类型别名包路径

# ========================日志配置=======================
logging:
  level:
    com.xx: info  # 设置特定包的日志级别

# ========================JSON 日期格式配置===================
---
spring:
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss  # JSON 日期格式
    time-zone: GMT+8  # 设置时区为 GMT+8

10、登录页面

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Demo</title>
    <!-- 请勿在项目正式环境中引用该 layui.css 地址 -->
    <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>

<!-- 请勿在项目正式环境中引用该 layui.js 地址 -->
<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; // 获取表单字段值
            // 此处可执行 Ajax 等操作
            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; // 阻止默认 form 跳转
        });
    });
</script>

</body>
</html>

网站公告

今日签到

点亮在社区的每一天
去签到