feat(auth): 支持管理员初始化时使用用户ID和手机号
This commit is contained in:
parent
6a2bf4f0f2
commit
c934ccb10d
@ -1,4 +1,5 @@
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
import re
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, HTTPException, Request, status, UploadFile, File
|
from fastapi import APIRouter, Depends, HTTPException, Request, status, UploadFile, File
|
||||||
from fastapi.security import OAuth2PasswordRequestForm
|
from fastapi.security import OAuth2PasswordRequestForm
|
||||||
@ -60,8 +61,9 @@ class UserResponse(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
class InitializeAdmin(BaseModel):
|
class InitializeAdmin(BaseModel):
|
||||||
username: str
|
user_id: str # 直接输入用户ID
|
||||||
password: str
|
password: str
|
||||||
|
phone_number: str | None = None
|
||||||
|
|
||||||
|
|
||||||
class UsernameValidation(BaseModel):
|
class UsernameValidation(BaseModel):
|
||||||
@ -182,21 +184,34 @@ async def initialize_admin(admin_data: InitializeAdmin, db: Session = Depends(ge
|
|||||||
# 创建管理员账户
|
# 创建管理员账户
|
||||||
hashed_password = AuthUtils.hash_password(admin_data.password)
|
hashed_password = AuthUtils.hash_password(admin_data.password)
|
||||||
|
|
||||||
# 验证用户名
|
# 验证用户ID格式(只支持字母数字和下划线)
|
||||||
is_valid, error_msg = validate_username(admin_data.username)
|
if not re.match(r'^[a-zA-Z0-9_]+$', admin_data.user_id):
|
||||||
if not is_valid:
|
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_400_BAD_REQUEST,
|
status_code=status.HTTP_400_BAD_REQUEST,
|
||||||
detail=error_msg,
|
detail="用户ID只能包含字母、数字和下划线",
|
||||||
)
|
)
|
||||||
|
|
||||||
# 生成user_id
|
if len(admin_data.user_id) < 3 or len(admin_data.user_id) > 20:
|
||||||
existing_user_ids = []
|
raise HTTPException(
|
||||||
user_id = generate_unique_user_id(admin_data.username, existing_user_ids)
|
status_code=status.HTTP_400_BAD_REQUEST,
|
||||||
|
detail="用户ID长度必须在3-20个字符之间",
|
||||||
|
)
|
||||||
|
|
||||||
|
# 验证手机号格式(如果提供了)
|
||||||
|
if admin_data.phone_number and not is_valid_phone_number(admin_data.phone_number):
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_400_BAD_REQUEST,
|
||||||
|
detail="手机号格式不正确"
|
||||||
|
)
|
||||||
|
|
||||||
|
# 由于是首次初始化,直接使用输入的user_id
|
||||||
|
user_id = admin_data.user_id
|
||||||
|
|
||||||
new_admin = User(
|
new_admin = User(
|
||||||
username=admin_data.username,
|
username=admin_data.user_id, # username和user_id设置为相同值
|
||||||
user_id=user_id,
|
user_id=user_id,
|
||||||
|
phone_number=admin_data.phone_number,
|
||||||
|
avatar=None, # 初始化时头像为空
|
||||||
password_hash=hashed_password,
|
password_hash=hashed_password,
|
||||||
role="superadmin",
|
role="superadmin",
|
||||||
last_login=datetime.now()
|
last_login=datetime.now()
|
||||||
|
|||||||
@ -222,7 +222,7 @@ const currentAgentId = computed(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const currentAgentMetadata = computed(() => {
|
const currentAgentMetadata = computed(() => {
|
||||||
if (agentStore?.metadata && CurrentAgentId.value in agentStore?.metadata[currentAgentId.value]) {
|
if (agentStore?.metadata && currentAgentId.value in agentStore?.metadata[currentAgentId.value]) {
|
||||||
return agentStore?.metadata[currentAgentId.value]
|
return agentStore?.metadata[currentAgentId.value]
|
||||||
}
|
}
|
||||||
return {}
|
return {}
|
||||||
|
|||||||
@ -176,7 +176,7 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { reactive, onMounted, watch } from 'vue';
|
import { reactive, onMounted, watch } from 'vue';
|
||||||
import { notification } from 'ant-design-vue';
|
import { notification, Modal } from 'ant-design-vue';
|
||||||
import { useUserStore } from '@/stores/user';
|
import { useUserStore } from '@/stores/user';
|
||||||
import {
|
import {
|
||||||
DeleteOutlined,
|
DeleteOutlined,
|
||||||
@ -423,9 +423,7 @@ const confirmDeleteUser = (user) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 确认对话框
|
// 确认对话框
|
||||||
const { modal } = notification;
|
Modal.confirm({
|
||||||
|
|
||||||
modal.confirm({
|
|
||||||
title: '确认删除用户',
|
title: '确认删除用户',
|
||||||
content: `确定要删除用户 "${user.username}" 吗?此操作不可撤销。`,
|
content: `确定要删除用户 "${user.username}" 吗?此操作不可撤销。`,
|
||||||
okText: '删除',
|
okText: '删除',
|
||||||
|
|||||||
@ -39,8 +39,12 @@
|
|||||||
|
|
||||||
<!-- 初始化管理员表单 -->
|
<!-- 初始化管理员表单 -->
|
||||||
<div v-if="isFirstRun" class="login-form">
|
<div v-if="isFirstRun" class="login-form">
|
||||||
<h2>系统初始化</h2>
|
<h2>系统初始化,请创建超级管理员账户</h2>
|
||||||
<p class="init-desc">系统首次运行,请创建超级管理员账户:</p>
|
<div class="init-tips">
|
||||||
|
<p>• 请输入用户ID(仅支持字母、数字和下划线)</p>
|
||||||
|
<p>• 手机号可选填写,用于后续登录</p>
|
||||||
|
<p>• 请妥善保管管理员账户信息</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<a-form
|
<a-form
|
||||||
:model="adminForm"
|
:model="adminForm"
|
||||||
@ -48,11 +52,50 @@
|
|||||||
layout="vertical"
|
layout="vertical"
|
||||||
>
|
>
|
||||||
<a-form-item
|
<a-form-item
|
||||||
label="用户名"
|
label="用户ID"
|
||||||
name="username"
|
name="user_id"
|
||||||
:rules="[{ required: true, message: '请输入用户名' }]"
|
:rules="[
|
||||||
|
{ required: true, message: '请输入用户ID' },
|
||||||
|
{
|
||||||
|
pattern: /^[a-zA-Z0-9_]+$/,
|
||||||
|
message: '用户ID只能包含字母、数字和下划线'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
min: 3,
|
||||||
|
max: 20,
|
||||||
|
message: '用户ID长度必须在3-20个字符之间'
|
||||||
|
}
|
||||||
|
]"
|
||||||
>
|
>
|
||||||
<a-input v-model:value="adminForm.username" prefix-icon="user" />
|
<a-input
|
||||||
|
v-model:value="adminForm.user_id"
|
||||||
|
placeholder="请输入用户ID(3-20个字符)"
|
||||||
|
:maxlength="20"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item
|
||||||
|
label="手机号(可选)"
|
||||||
|
name="phone_number"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
validator: async (rule, value) => {
|
||||||
|
if (!value || value.trim() === '') {
|
||||||
|
return; // 空值允许
|
||||||
|
}
|
||||||
|
const phoneRegex = /^1[3-9]\d{9}$/;
|
||||||
|
if (!phoneRegex.test(value)) {
|
||||||
|
throw new Error('请输入正确的手机号格式');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="adminForm.phone_number"
|
||||||
|
placeholder="可用于登录,可不填写"
|
||||||
|
:max-length="11"
|
||||||
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
<a-form-item
|
<a-form-item
|
||||||
@ -175,7 +218,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, reactive, onMounted, onUnmounted, computed } from 'vue';
|
import { ref, reactive, onMounted, onUnmounted, computed, watch } from 'vue';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import { useUserStore } from '@/stores/user';
|
import { useUserStore } from '@/stores/user';
|
||||||
import { useInfoStore } from '@/stores/info';
|
import { useInfoStore } from '@/stores/info';
|
||||||
@ -215,9 +258,10 @@ const loginForm = reactive({
|
|||||||
|
|
||||||
// 管理员初始化表单
|
// 管理员初始化表单
|
||||||
const adminForm = reactive({
|
const adminForm = reactive({
|
||||||
username: '',
|
user_id: '', // 改为直接输入user_id
|
||||||
password: '',
|
password: '',
|
||||||
confirmPassword: ''
|
confirmPassword: '',
|
||||||
|
phone_number: '' // 手机号字段(可选)
|
||||||
});
|
});
|
||||||
|
|
||||||
// 开发中功能提示
|
// 开发中功能提示
|
||||||
@ -269,14 +313,13 @@ const formatTime = (seconds) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 密码确认验证
|
// 密码确认验证
|
||||||
const validateConfirmPassword = (rule, value) => {
|
const validateConfirmPassword = async (rule, value) => {
|
||||||
if (value === '') {
|
if (value === '') {
|
||||||
return Promise.reject('请确认密码');
|
throw new Error('请确认密码');
|
||||||
}
|
}
|
||||||
if (value !== adminForm.password) {
|
if (value !== adminForm.password) {
|
||||||
return Promise.reject('两次输入的密码不一致');
|
throw new Error('两次输入的密码不一致');
|
||||||
}
|
}
|
||||||
return Promise.resolve();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 处理登录
|
// 处理登录
|
||||||
@ -388,8 +431,9 @@ const handleInitialize = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await userStore.initialize({
|
await userStore.initialize({
|
||||||
username: adminForm.username,
|
user_id: adminForm.user_id,
|
||||||
password: adminForm.password
|
password: adminForm.password,
|
||||||
|
phone_number: adminForm.phone_number || null // 空字符串转为null
|
||||||
});
|
});
|
||||||
|
|
||||||
message.success('管理员账户创建成功');
|
message.success('管理员账户创建成功');
|
||||||
@ -624,10 +668,28 @@ onUnmounted(() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.init-desc {
|
.init-tips {
|
||||||
margin-bottom: 24px;
|
background: #f6ffed;
|
||||||
color: #666;
|
border: 1px solid #b7eb8f;
|
||||||
text-align: center;
|
border-radius: 6px;
|
||||||
|
padding: 12px 16px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 4px 0;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #52c41a;
|
||||||
|
line-height: 1.4;
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.error-message {
|
.error-message {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user