引言:如果要实现一个ai人工助手,我们可以简单参考一下DeepSeek,KiMi,看看里面比较重要的功能有那些:
登录,注册是使用每一个ai助手必不可少的功能!当然登录,注册里面的功能也不少,比如说:”忘记密码,重置密码“,”用户名和密码的要求设置(用户名不能少于6个字符,密码需要数字加英文字母,而且不能太过简单,并且要输入两遍密码,进行判断)“。
注册也可以分为用户名注册和邮箱注册,当然你也可以使用(电话号码注册,和微信,QQ扫码登录),但是这些部分需要一些¥,所以在本次零成本的项目中,咱就先不配置。
Ai的多功能的对话:当用户完成了登录后,就可以使用我们的Ai对话了,里面也会有一些细节的功能,比如说:对于每个用户的历史会话进行保存,可以新建对话,删除对话,当然也可以修改对话的名称(默认是以用户第一次输入的问题,做为对话的名称),对于Ai呢,我们也可以添加联网搜索,深度思考,直接发送文件,语音输入等。
好~,大概项目的框架我们理清除了,接下来我们就开始将这个项目一步步的写出来。
最后会附赠源码哟
写代码项目之前,要把我们的目录先创建好:
后端里面主要是一个.env目录和main.py的主文件:
前端里面主要是html,css,还有js:
最后建好了之后就是这样:
注册与邮箱登录
注册页面:register.html
前端页面我们先写一个最普通的注册界面:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>注册 - DeepSeek</title>
</head>
<body>
<div class="header">
<a class="logo" href="#">
Ai助手
</a>
</div>
<div class="container">
<div class="card">
<div class="card-title">用户注册</div>
<div class="form-container">
<label for="username">用户名</label>
<input class="input-style" type="text" id="username" name="username" placeholder="请输入用户名" required>
<label for="password">密码</label>
<input class="input-style" type="password" id="password" name="password" placeholder="请输入密码" required>
<label for="confirm">确认密码</label>
<input class="input-style" type="password" id="confirm" name="confirm" placeholder="请再次输入密码" required>
<button class="btn-style" type="submit" id="zc">注册</button>
</div>
<div class="reg-link">
已有账号?<a href="login.html">返回登录</a>
</div>
</div>
</div>
</body>
</html>
这是一个最普通的注册界面,运行之后:
十分的简陋,所以我们需要给他加样式
注册页面css样式
因为我嫌麻烦所以将html和css写在了一起,并没有将样式写在scc目录下面
<style>
:root {
--primary: #2563eb;
--background: #f8fafc;
--input-bg: #f1f5f9;
--border: #e2e8f0;
--shadow: 0 4px 24px 0 rgba(0,0,0,0.07);
}
body {
margin: 0;
font-family: "Inter", "微软雅黑", Arial, sans-serif;
background: var(--background);
color: #1e293b;
}
.header {
height: 60px;
display: flex;
align-items: center;
padding: 0 40px;
background: #ffffffcc;
box-shadow: 0 1px 6px 0 rgba(0,0,0,0.02);
position: sticky;
top: 0;
backdrop-filter: blur(12px);
}
.logo {
display: flex;
align-items: center;
font-weight: 700;
font-size: 1.3rem;
color: var(--primary);
letter-spacing: 1px;
text-decoration: none;
}
.logo svg {
width: 32px;
height: 32px;
margin-right: 8px;
}
.container {
min-height: 85vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 40px 16px 0 16px;
}
.card {
background: #fff;
border-radius: 18px;
box-shadow: var(--shadow);
border: 1px solid var(--border);
width: 380px;
max-width: 97vw;
padding: 36px;
margin-top: 34px;
display: flex;
flex-direction: column;
gap: 14px;
}
.card-title {
font-size: 1.32rem;
font-weight: 700;
margin-bottom: 20px;
text-align: center;
}
form label {
font-size: 1rem;
margin-bottom: 6px;
font-weight: 500;
display: block;
}
.input-style {
width: 92%;
padding: 12px 14px;
border-radius: 11px;
border: 1.2px solid var(--border);
background: var(--input-bg);
font-size: 1.02rem;
margin-bottom: 16px;
outline: none;
transition: border 0.2s;
}
.input-style:focus {
border: 1.5px solid var(--primary);
}
.btn-style {
width: 100%;
background: var(--primary);
color: #fff;
font-size: 1.06rem;
font-weight: 700;
border: none;
border-radius: 11px;
padding: 13px 0;
margin-top: 10px;
cursor: pointer;
letter-spacing: 1px;
}
.btn-style:active {
background: #1a45b6;
}
.reg-link {
text-align: center;
font-size: 0.97rem;
margin-top: 20px;
color: #64748b;
}
.reg-link a {
color: var(--primary);
text-decoration: none;
font-weight: 600;
}
@media (max-width: 480px) {
.card { padding: 16px 6vw; }
}
</style>
添加了样式之后我们的注册界面就变的好看多了:
邮箱注册
前面我们不是说还要进行邮箱注册吗?但是这里只有用户注册!
所以我们加一个<div>
<style>
.form-container {
display: flex;
flex-direction: column;
gap: 14px;
}
.input-style {
width: 92%;
padding: 12px 14px;
border-radius: 11px;
border: 1.2px solid var(--border);
background: var(--input-bg);
font-size: 1.02rem;
margin-bottom: 16px;
outline: none;
transition: border 0.2s;
}
.input-style:focus {
border: 1.5px solid var(--primary);
}
.btn-style {
width: 100%;
background: var(--primary);
color: #fff;
font-size: 1.06rem;
font-weight: 700;
border: none;
border-radius: 11px;
padding: 13px 0;
margin-top: 10px;
cursor: pointer;
letter-spacing: 1px;
}
.btn-style:active {
background: #1a45b6;
}
</style>
将邮箱注册隐藏
<!-- 邮箱验证码登录表单 -->
<div id="login-form" class="form-container" style="display:none;">
<label for="email-login">邮箱</label>
<input class="input-style" type="email" id="email-login" name="email-login" placeholder="请输入邮箱">
<button class="btn-style" type="button" id="get_code_btn">获取验证码</button>
<label for="code">验证码</label>
<input class="input-style" type="text" id="code" name="code" placeholder="请输入验证码">
<button class="btn-style" type="button" id="login_btn">登录</button>
</div>
提醒:你把这部分代码加上的时候,可不会显示哟!
style="display:none;"
当然这是故意这样设置的,因为我们到时候需要的样式:
这部分的样式是隐藏的,只有你点击邮箱验证码登录时,才会出现,随之注册的<div>就会被隐藏
进行这一步就需要用到js了
通过JavaScript动态显示
document.getElementById('register-tab').addEventListener('click', function() {
// 显示注册表单
document.getElementById('register-form').style.display = 'block';
// 隐藏登录表单
document.getElementById('login-form').style.display = 'none';
// 给注册标签添加激活样式
document.getElementById('register-tab').classList.add('active-tab');
// 移除登录标签的激活样式
document.getElementById('login-tab').classList.remove('active-tab');
});
document.getElementById('login-tab').addEventListener('click', function() {
// 隐藏注册表单
document.getElementById('register-form').style.display = 'none';
// 显示登录表单
document.getElementById('login-form').style.display = 'block';
// 给登录标签添加激活样式
document.getElementById('login-tab').classList.add('active-tab');
// 移除注册标签的激活样式
document.getElementById('register-tab').classList.remove('active-tab');
});
这是关键性代码 可以进行切换。
细节代码:
注意:有一些细节要注意:
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script src="../js/register.js"></script>
这是针对移动端网页设计的标签,用于控制视口的行为。确保网页在不同设备上能够正确缩放,使网页宽度等于设备宽度(width=device-width),并且初始缩放比例为1.0(initial-scale=1.0),避免移动端浏览器默认的缩放行为导致页面过小。
引入了axios库,它是一个基于Promise的HTTP客户端,用于浏览器和node.js中。通常用于发送AJAX请求。
在html里面导入js,不然html是无法响应的
到此恭喜你已经完成前端页面,只差js里面的功能了
当然现在你点击是无效的,因为这部分代码我们还没有实现,要在js里面进行功能实现
注册页面js代码
document.addEventListener("DOMContentLoaded", function () {
let username = document.getElementById("username");
let password = document.getElementById("password");
let email = document.getElementById("email");
let confirm = document.getElementById("confirm");
let zc = document.getElementById("zc");
}
监听页面加载完成
首先我们将HTML里面的标签id全部拿到,并用变量装起来。
分别对应:用户名输入框、密码输入框、确认密码输入框、注册按钮。
zc.addEventListener("click", function (e) {
e.preventDefault();
// 阻止按钮默认提交表单行为
if(password.value !== confirm.value) {
alert("两次输入的密码不一致");
return;
}
axios.post("http://127.0.0.1:8000/register", {
username: username.value,
password: password.value
}).then(res => {
if(res.data.code === 200){
alert("注册成功");
window.location.href = "login.html";
}else{
alert(res.data.message || "注册失败");
}
}).catch(err => {
alert(err.message);
console.log(err);
});
});

preventDefault(){code: 200, message: "xxx"}
现在我们是完成了最简答的注册,当然我们也可以添加一些限制要求(用户名不能少于6个字符,密码需要数字加英文字母且不少于8个,而且不能太过简单)
/* 1. 用户名长度 ≥ 6 */
if (u.length < 6) {
alert("用户名不能少于 6 个字符");
return;
}
/* 2. 密码长度 ≥ 8,且必须同时包含数字和英文字母 */
if (!/^(?=.*[0-9])(?=.*[a-zA-Z]).{8,}$/.test(p)) {
alert("密码必须包含数字和英文字母,且不少于 8 位");
return;
}
/* 3. 不能过于简单:这里给一个示例黑名单 */
const weakPwd = ["12345678", "password", "abcdefg", "00000000"];
if (weakPwd.includes(p.toLowerCase())) {
alert("密码过于简单,请更换");
return;
}
/* 4. 两次密码一致 */
if (p !== c) {
alert("两次输入的密码不一致");
return;
}
邮箱登录js代码
// 邮箱格式验证
let emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailPattern.test(email.value)) {
alert("请输入有效的邮箱地址");
return;
}
// 获取验证码逻辑
document.getElementById("get_code_btn").addEventListener("click", function() {
let emailLogin = document.getElementById("email-login").value;
if (!emailLogin) {
alert("请输入邮箱!");
return;
}
// 发送获取验证码请求
axios.post("http://127.0.0.1:8000/send_email_code", { email: emailLogin }).then(res => {
alert(res.data.message);
}).catch(err => {
alert("获取验证码失败:" + err.message);
});
/^[^\s@]+@[^\s@]+\.[^\s@]+$/email.value@.get_code_btnemail-login{ email: emailLogin }http://127.0.0.1:8000/send_email_code
邮箱验证码登录逻辑:
document.getElementById("login_btn").addEventListener("click", function() {
let emailLogin = document.getElementById("email-login").value;
let code = document.getElementById("code").value;
if (!emailLogin || !code) {
alert("请填写完整信息!");
return;
}
// 发送登录请求
axios.post("http://127.0.0.1:8000/login_by_code", { email: emailLogin, code: code }).then(res => {
if(res.data.code === 200) {
alert("登录成功");
// 可以存储token或者用户信息,然后跳转
window.location.href = "../html/chat.html"; // 假设登录成功后跳转到首页
} else {
alert(res.data.message || "登录失败");
}
}).catch(err => {
alert("登录请求失败:" + err.message);
console.log(err);
});
});
});
login_btnemailLogincode/login_by_code{ email, code }{ code: 200, message: "ok", token: "..." }{ code: 4xx/5xx, message: "验证码错误" }
好!到此注册页面的前端代码我们已经完成了 接下来我们要开始完成后端的代码了!
后端main.py:
不要忘记,我们是这些用户,密码,邮箱都是用数据库来存储的,所以我们的第一步是创建数据库。
先导入模块:
import os
import sqlite3
from fastapi import FastAPI
初始化 FastAPI 应用:
app = FastAPI()
创建跨域:
给应用添加一个 跨域资源共享中间件,让浏览器不再拦截前端
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # 允许所有源
allow_credentials=True,
allow_methods=["*"], # 允许所有HTTP方法
allow_headers=["*"], # 允许所有HTTP头部
)
然后可以开始创建数据库:
连接(不存在则自动创建 users.db)
② 获取游标
③ 建 users 表④ 建 conversations 表
⑤ 建 messages 表
⑥ 提交事务
⑦ 关闭连接
def init():
conn = sqlite3.connect('users.db')
cursor = conn.cursor()
cursor.execute('''
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
password TEXT NOT NULL,
email TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)
''')
cursor.execute('''
CREATE TABLE IF NOT EXISTS conversations (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
title TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY(user_id) REFERENCES users(id)
)
''')
cursor.execute('''
CREATE TABLE IF NOT EXISTS messages (
id INTEGER PRIMARY KEY AUTOINCREMENT,
conversation_id INTEGER NOT NULL,
role TEXT,
content TEXT,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY(conversation_id) REFERENCES conversations(id)
)
''')
conn.commit()
conn.close()
init()
到此我们的数据库已经建好了,接下来就是,开始写后端逻辑代码了
注册页面的后端代码:
class User(BaseModel):
username: str
password: str
email: EmailStr
@app.post('/register')
async def register(user: User):
conn = sqlite3.connect('users.db')
cursor = conn.cursor()
try:
hashed_password = bcrypt.hashpw(user.password.encode('utf-8'), bcrypt.gensalt())
cursor.execute('INSERT INTO users (username,password,email) VALUES (?, ? ,?)',
(user.username,hashed_password.decode('utf-8'),user.email))
user_id = cursor.lastrowid
conn.commit()
return {
"code":200,
"message":"注册成功",
"data":{
"id":user_id,
"username":user.username,
"email":user.email
}
}
except Exception as e:
conn.rollback()
error_msg = str(e)
if "UNIQUE constraint failed: users.username" in error_msg:
return {
"code": 400,
"message": "用户名已注册,请更换其他用户名",
"data": {}
}
return {
"code":500,
"message":"插入数据失败",
"data":{
"error":str(e)
}
}
finally:
conn.close()
根据这个代码,我详细解释一下,FastAPI接口
首先定义根路径 / 的路由操作:
@app.post('/register')
async def register(user: User):
POST /registeruser: UserUserusernamepasswordemail
连接数据库
conn = sqlite3.connect('users.db')
cursor = conn.cursor()
密码加密(哈希加密)
hashed_password = bcrypt.hashpw(user.password.encode('utf-8'), bcrypt.gensalt())
bcrypt.decode()
插入用户
cursor.execute(
'INSERT INTO users (username, password, email) VALUES (?, ?, ?)',
(user.username, hashed_password.decode(), user.email)
)
user_id = cursor.lastrowid
conn.commit()
?cursor.lastrowididcommit()
运行成功返回
return {
"code": 200,
"message": "注册成功",
"data": {
"id": user_id,
"username": user.username,
"email": user.email
}
}
接下来,进行一些异常处理的情况
用户名重复
if "UNIQUE constraint failed: users.username" in str(e):
return {
"code": 400,
"message": "用户名已注册,请更换其他用户名",
"data": {}
}
其他错误
return {
"code": 500,
"message": "插入数据失败",
"data": { "error": str(e) }
}
对于这段代码的总结:
用户提交 JSON → 密码加盐哈希 → 插入 SQLite → 成功返回 200 & 用户信息,失败返回 400/500 并回滚。
接下来是难点,邮箱的后端代码!
邮箱登录的后端代码
/send_email_code
@app.post("/send_email_code")
async def send_email_code_api(data: EmailCodeRequest):
email = data.email
code = generate_code()
expire = int(time.time()) + 300 # 5分钟有效
try:
send_email_code(email, code)
email_code_cache[email] = (code, expire)
return {"code": 200, "message": "验证码已发送"}
except Exception as e:
print(f"邮件发送失败: {str(e)}")
return {"code": 500, "message": "邮件发送失败", "detail": str(e)}
创建发送验证码的后端接口,随机生成6位数字/字母,设置5分钟过期时间,调用(一段函数)发送邮件出去,将验证码等放入一个字典,让前端提示:验证码已发送
@app.post("/send_email_code")data: EmailCodeRequestemailcode = generate_code()expire = int(time.time()) + 300send_email_code(email, code)email_code_cache[email] = (code, expire)return {"code": 200, ...}except Exception as e
/login_by_code ——「用验证码登录」
@app.post("/login_by_code")
async def login_by_code_api(data: EmailCodeLoginRequest):
email = data.email
code = data.code
now = int(time.time())
if email not in email_code_cache:
return {"code": 400, "message": "请先获取验证码"}
right_code, expire = email_code_cache[email]
if now > expire:
return {"code": 400, "message": "验证码已过期"}
if code != right_code:
return {"code": 400, "message": "验证码错误"}
# 查找用户,不存在则注册
conn = sqlite3.connect('users.db')
cursor = conn.cursor()
cursor.execute("SELECT id FROM users WHERE email=?", (email,))
row = cursor.fetchone()
if not row:
username = email.split('@')[0]
cursor.execute('INSERT INTO users (username, password, email) VALUES (?, ?, ?)', (username, '', email))
user_id = cursor.lastrowid
conn.commit()
else:
user_id = row[0]
conn.close()
# 清除验证码
email_code_cache.pop(email, None)
return {"code": 200, "message": "登录成功", "data": {"user_id": user_id, "email": email}}
校验验证码 → 过期/错误直接 400;正确则「找到用户 or 自动注册」→ 返回 200 和用户信息。
@app.post("/login_by_code")data: EmailCodeLoginRequestemailcodenow = int(time.time())if email not in email_code_cachenow > expirecode != right_codeSELECT id FROM users WHERE email=?if not row:username = email.split('@')[0]INSERT INTO users ...email_code_cache.pop(email, None)return {"code": 200, "user_id": ..., "email": ...}
用 SMTP 协议发送纯文本验证码邮件的函数
def send_email_code(to_email, code):
smtp_server = "######.com" # 换成你实际用的
smtp_port = 465
from_addr = "#######.com" #换成你的邮箱
password = "########" # 注意不是邮箱密码,是SMTP授权码
msg = MIMEText(f"你的验证码是:{code},5分钟内有效。", "plain", "utf-8")
msg["Subject"] = "验证码登录"
msg["From"] = from_addr
msg["To"] = to_email
s = smtplib.SMTP_SSL(smtp_server, smtp_port)
s.login(from_addr, password)
s.sendmail(from_addr, [to_email], msg.as_string())
s.quit()
def send_email_code(to_email, code):smtp_server = "######.com"smtp.qq.comsmtpdm.aliyun.comsmtp.163.comsmtp_port = 465from_addr = "#######.com"password = "########"msg = MIMEText(...)"plain"{code}utf-8msg["Subject"] = "验证码登录"msg["From"] / msg["To"]s = smtplib.SMTP_SSL(...)s.login(from_addr, password)s.sendmail(...)s.quit()
到此我们的注册与邮箱登录都已完成!展示!
注册以后会跳转登录页面
接下来我们开始完成登录界面
登录界面
登录界面:login.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>注册 - DeepSeek</title>
</head>
<body>
<div class="header">
<a class="logo" href="#">
<svg viewBox="0 0 40 40" fill="none">
<circle cx="20" cy="20" r="18" fill="#2563eb" opacity="0.15"/>
<circle cx="20" cy="20" r="12.5" fill="#2563eb" opacity="0.28"/>
<circle cx="20" cy="20" r="7.5" fill="#2563eb"/>
</svg>
Hazelight
</a>
</div>
<div class="container">
<div class="card">
<div class="card-title">账号登录</div>
<div class="form-container" autocomplete="off">
<label for="username">用户名</label>
<input class="input-style" type="text" id="username" name="username" placeholder="请输入用户名" required>
<label for="password">密码</label>
<input class="input-style" type="password" id="password" name="password" placeholder="请输入密码" required>
<button class="btn-style" type="button" id="dl">登录</button>
</div>
<div class="reg-link">
没有账号?<a href="register.html">立即注册</a>
</div>
</div>
</div>
</body>
</html>
这是简单的登录界面
登录界面:login.css
接下来我们给他加上css样式渲染
:root {
--primary: #2563eb;
--background: #f8fafc;
--input-bg: #f1f5f9;
--border: #e2e8f0;
--shadow: 0 4px 24px 0 rgba(0,0,0,0.07);
}
body {
margin: 0;
font-family: "Inter", "微软雅黑", Arial, sans-serif;
background: var(--background);
color: #1e293b;
}
.header {
height: 60px;
display: flex;
align-items: center;
padding: 0 40px;
background: #ffffffcc;
box-shadow: 0 1px 6px 0 rgba(0,0,0,0.02);
position: sticky;
top: 0;
backdrop-filter: blur(12px);
}
.logo {
display: flex;
align-items: center;
font-weight: 700;
font-size: 1.3rem;
color: var(--primary);
letter-spacing: 1px;
text-decoration: none;
}
.logo svg {
width: 32px;
height: 32px;
margin-right: 8px;
}
.container {
min-height: 85vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 40px 16px 0 16px;
}
.card {
background: #fff;
border-radius: 18px;
box-shadow: var(--shadow);
border: 1px solid var(--border);
width: 360px;
max-width: 97vw;
padding: 36px;
margin-top: 34px;
display: flex;
flex-direction: column;
gap: 14px;
}
.card-title {
font-size: 1.35rem;
font-weight: 700;
margin-bottom: 20px;
text-align: center;
}
form label {
font-size: 1rem;
margin-bottom: 6px;
font-weight: 500;
display: block;
}
.input-style {
width: 92%;
padding: 12px 14px;
border-radius: 11px;
border: 1.2px solid var(--border);
background: var(--input-bg);
font-size: 1.02rem;
margin-bottom: 16px;
outline: none;
transition: border 0.2s;
}
.input-style:focus {
border: 1.5px solid var(--primary);
}
.btn-style {
width: 100%;
background: var(--primary);
color: #fff;
font-size: 1.06rem;
font-weight: 700;
border: none;
border-radius: 11px;
padding: 13px 0;
margin-top: 10px;
cursor: pointer;
letter-spacing: 1px;
}
.btn-style:active {
background: #1a45b6;
}
.reg-link {
text-align: center;
font-size: 0.98rem;
margin-top: 20px;
color: #64748b;
}
.reg-link a {
color: var(--primary);
text-decoration: none;
font-weight: 600;
}
@media (max-width: 480px) {
.card { padding: 16px 6vw; }
}
经过css渲染之后
界面就变的好看了
接下来就是让前端的一些功能
登录界面:login.js
document.addEventListener('DOMContentLoaded', function() {
const username = document.getElementById('username');
const password = document.getElementById('password');
const dl = document.getElementById('dl');
dl.addEventListener('click', function() {
if(!username.value || !password.value) {
alert('请输入用户名和密码');
return;
}
axios.post('http://127.0.0.1:8000/login', {
username: username.value,
password: password.value
}).then(res => {
if(res.data.code === 200){
alert("登录成功");
window.location.href = "chat.html";
}else{
alert(res.data.message || "登录失败");
}
}).catch(err => {
alert("登录请求失败");
console.log(err);
});
});
});
登录界面的js非常简单,并没有特别难点,将用户密码,传入后端,后端接受后进行判断,成功后就登录成功,失败后就输出登录失败
登录界面:后端main.py
接下来进行前后端的交互,后端也是非常的简单
@app.post('/login')
async def login(user: User):
conn = sqlite3.connect('users.db')
cursor = conn.cursor()
try:
cursor.execute(
'SELECT id, username, password FROM users WHERE username = ?',
(user.username,)
)
result = cursor.fetchone()
if result:
stored_hash = result[2]
if bcrypt.checkpw(user.password.encode('utf-8'), stored_hash.encode('utf-8')):
return {
"code": 200,
"message": "登录成功",
"data": {
"id": result[0],
"username": result[1]
}
}
return {
"code": 400,
"message": "用户名或密码错误",
"data": {}
}
except Exception as e:
return {
"code": 500,
"message": "登录异常",
"data": {"error": str(e)}
}
finally:
conn.close()
根据用户名查库 → 用 bcrypt 比对密码 → 正确返回 200 + 用户信息,错误返回 400/500,始终关闭数据库连接。
先到这里,我们到时候接着补充