feat:注册接口对接
This commit is contained in:
parent
7911565249
commit
7551571f0a
@ -21,7 +21,7 @@ export const API_ENDPOINTS = {
|
||||
AUTH: {
|
||||
LOGIN: '/aiol/aiolUser/login',
|
||||
USER_INFO: '/aiol/aiolUser/info',
|
||||
REGISTER: '/auth/register',
|
||||
REGISTER: '/aiol/aiolUser/register',
|
||||
LOGOUT: '/auth/logout',
|
||||
REFRESH: '/auth/refresh',
|
||||
ME: '/auth/me',
|
||||
|
@ -7,6 +7,7 @@ import type {
|
||||
LoginResponse,
|
||||
// BackendLoginResponse,
|
||||
RegisterRequest,
|
||||
BackendRegisterRequest,
|
||||
UserProfile,
|
||||
BackendUserInfo,
|
||||
UserInfoResponse,
|
||||
@ -198,8 +199,59 @@ export class AuthApi {
|
||||
}
|
||||
|
||||
// 用户注册
|
||||
static register(data: RegisterRequest): Promise<ApiResponse<User>> {
|
||||
return ApiRequest.post('/auth/register', data)
|
||||
static async register(data: RegisterRequest): Promise<ApiResponse<User>> {
|
||||
try {
|
||||
// 转换为后端接口格式
|
||||
const registerData: BackendRegisterRequest = {
|
||||
studentNumber: data.email || data.phone || data.username,
|
||||
inviteCode: data.inviteCode || '',
|
||||
password: data.password
|
||||
}
|
||||
|
||||
console.log('🚀 发送注册请求:', { url: '/aiol/aiolUser/register', data: { ...registerData, password: '***' } })
|
||||
|
||||
// 调用后端注册API
|
||||
const response = await ApiRequest.post<any>('/aiol/aiolUser/register', registerData)
|
||||
|
||||
console.log('🔍 Register API Response:', response)
|
||||
|
||||
// 处理响应格式
|
||||
let actualCode = response.code
|
||||
let actualMessage = response.message
|
||||
|
||||
// 如果响应被包装在data中
|
||||
if (response.data && typeof response.data === 'object' && 'code' in response.data) {
|
||||
actualCode = response.data.code
|
||||
actualMessage = response.data.message
|
||||
}
|
||||
|
||||
// 检查注册是否成功
|
||||
if (actualCode === 200 || actualCode === 0) {
|
||||
// 注册成功,返回标准格式
|
||||
const adaptedResponse: ApiResponse<User> = {
|
||||
code: 200,
|
||||
message: actualMessage || '注册成功',
|
||||
data: {
|
||||
id: Date.now(), // 临时ID
|
||||
username: registerData.studentNumber,
|
||||
email: data.email || '',
|
||||
phone: data.phone || '',
|
||||
nickname: registerData.studentNumber,
|
||||
avatar: '',
|
||||
role: 'student',
|
||||
status: 'active',
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString()
|
||||
}
|
||||
}
|
||||
return adaptedResponse
|
||||
} else {
|
||||
throw new Error(actualMessage || '注册失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('🚨 Register API Error:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
// 用户登出
|
||||
@ -266,6 +318,10 @@ export class AuthApi {
|
||||
static convertBackendUserToUser(backendUser: BackendUserInfo): User {
|
||||
const { baseInfo, roles, extendedInfo } = backendUser
|
||||
|
||||
console.log('🔄 convertBackendUserToUser - 原始后端数据:', backendUser)
|
||||
console.log('🔄 convertBackendUserToUser - baseInfo.avatar:', baseInfo.avatar)
|
||||
console.log('🔄 convertBackendUserToUser - baseInfo.realname:', baseInfo.realname)
|
||||
|
||||
// 转换性别
|
||||
let gender: 'male' | 'female' | 'other' = 'other'
|
||||
if (baseInfo.sex === 1) gender = 'male'
|
||||
@ -282,7 +338,7 @@ export class AuthApi {
|
||||
role = 'teacher'
|
||||
}
|
||||
|
||||
return {
|
||||
const convertedUser = {
|
||||
id: parseInt(baseInfo.id) || 0,
|
||||
username: baseInfo.username,
|
||||
email: baseInfo.email,
|
||||
@ -303,6 +359,11 @@ export class AuthApi {
|
||||
socialLinks: {}
|
||||
}
|
||||
}
|
||||
|
||||
console.log('✅ convertBackendUserToUser - 转换后的用户数据:', convertedUser)
|
||||
console.log('✅ convertBackendUserToUser - 转换后的头像:', convertedUser.avatar)
|
||||
|
||||
return convertedUser
|
||||
}
|
||||
|
||||
// 更新用户资料
|
||||
|
@ -127,7 +127,20 @@ export class ExamApi {
|
||||
*/
|
||||
static async createQuestion(data: CreateQuestionRequest): Promise<ApiResponse<string>> {
|
||||
console.log('🚀 添加题目:', data)
|
||||
const response = await ApiRequest.post<string>('/aiol/aiolQuestion/add', data)
|
||||
|
||||
// 提取repoId作为查询参数,其他数据作为请求体
|
||||
const { repoId, ...questionData } = data
|
||||
|
||||
// 构建查询参数(只有repoId)
|
||||
const params: any = {}
|
||||
if (repoId) {
|
||||
params.repoId = repoId
|
||||
}
|
||||
|
||||
console.log('🔍 API查询参数:', params)
|
||||
console.log('🔍 API请求体:', questionData)
|
||||
|
||||
const response = await ApiRequest.post<string>('/aiol/aiolQuestion/add', questionData, { params })
|
||||
console.log('✅ 添加题目成功:', response)
|
||||
return response
|
||||
}
|
||||
@ -161,7 +174,7 @@ export class ExamApi {
|
||||
*/
|
||||
static async createQuestionOption(data: CreateQuestionOptionRequest): Promise<ApiResponse<string>> {
|
||||
console.log('🚀 添加题目选项:', data)
|
||||
const response = await ApiRequest.post<string>('/gen/questionoption/questionOption/add', data)
|
||||
const response = await ApiRequest.post<string>('/aiol/aiolQuestionOption/add', data)
|
||||
console.log('✅ 添加题目选项成功:', response)
|
||||
return response
|
||||
}
|
||||
|
@ -123,6 +123,13 @@ export interface RegisterRequest {
|
||||
inviteCode?: string
|
||||
}
|
||||
|
||||
// 后端注册接口请求格式
|
||||
export interface BackendRegisterRequest {
|
||||
studentNumber: string
|
||||
inviteCode?: string
|
||||
password: string
|
||||
}
|
||||
|
||||
// 课程相关类型
|
||||
export interface Course {
|
||||
id: string // 改为string类型,保持后端ID的原始格式
|
||||
@ -773,12 +780,15 @@ export interface UpdateRepoRequest {
|
||||
}
|
||||
|
||||
export interface CreateQuestionRequest {
|
||||
repoId?: string
|
||||
parentId?: string
|
||||
type: number
|
||||
content: string
|
||||
analysis?: string
|
||||
difficulty: number
|
||||
score: number
|
||||
degree?: number
|
||||
ability?: number
|
||||
}
|
||||
|
||||
export interface UpdateQuestionRequest {
|
||||
|
@ -6,12 +6,14 @@
|
||||
height: size + 'px'
|
||||
}"
|
||||
>
|
||||
<img
|
||||
v-if="!imageError && src"
|
||||
:src="src"
|
||||
<img
|
||||
v-if="!imageError && src"
|
||||
:src="src"
|
||||
:alt="alt"
|
||||
@error="handleImageError"
|
||||
@load="handleImageLoad"
|
||||
crossorigin="anonymous"
|
||||
referrerpolicy="no-referrer"
|
||||
/>
|
||||
<div v-else class="avatar-placeholder">
|
||||
<span class="avatar-text">{{ avatarText }}</span>
|
||||
@ -20,7 +22,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import { ref, computed, watch } from 'vue'
|
||||
|
||||
interface Props {
|
||||
src?: string
|
||||
@ -38,6 +40,15 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
|
||||
const imageError = ref(false)
|
||||
|
||||
// 调试:监听src变化
|
||||
watch(() => props.src, (newSrc) => {
|
||||
console.log('🖼️ SafeAvatar - 头像URL变化:', newSrc)
|
||||
console.log('🖼️ SafeAvatar - 头像URL类型:', typeof newSrc)
|
||||
console.log('🖼️ SafeAvatar - 头像URL长度:', newSrc?.length)
|
||||
// 重置错误状态
|
||||
imageError.value = false
|
||||
}, { immediate: true })
|
||||
|
||||
// 根据用户名生成头像文字
|
||||
const avatarText = computed(() => {
|
||||
if (props.name) {
|
||||
@ -51,11 +62,13 @@ const avatarText = computed(() => {
|
||||
return '用'
|
||||
})
|
||||
|
||||
const handleImageError = () => {
|
||||
const handleImageError = (event: Event) => {
|
||||
console.error('🚨 SafeAvatar - 头像加载失败:', props.src, event)
|
||||
imageError.value = true
|
||||
}
|
||||
|
||||
const handleImageLoad = () => {
|
||||
console.log('✅ SafeAvatar - 头像加载成功:', props.src)
|
||||
imageError.value = false
|
||||
}
|
||||
</script>
|
||||
|
@ -162,6 +162,7 @@ const userStore = useUserStore()
|
||||
console.log('🔍 AppHeader - 当前用户信息:', userStore.user)
|
||||
console.log('🔍 AppHeader - 用户真实姓名:', userStore.user?.profile?.realName)
|
||||
console.log('🔍 AppHeader - 用户头像:', userStore.user?.avatar)
|
||||
console.log('🔍 AppHeader - 用户头像类型:', typeof userStore.user?.avatar)
|
||||
|
||||
// 监听用户信息变化
|
||||
watch(() => userStore.user, (newUser) => {
|
||||
@ -169,9 +170,15 @@ watch(() => userStore.user, (newUser) => {
|
||||
if (newUser) {
|
||||
console.log('🔄 AppHeader - 新的真实姓名:', newUser.profile?.realName)
|
||||
console.log('🔄 AppHeader - 新的头像:', newUser.avatar)
|
||||
console.log('🔄 AppHeader - 新的头像类型:', typeof newUser.avatar)
|
||||
}
|
||||
}, { deep: true })
|
||||
|
||||
// 监听头像变化
|
||||
watch(() => userStore.user?.avatar, (newAvatar) => {
|
||||
console.log('🖼️ AppHeader - 头像URL变化:', newAvatar)
|
||||
}, { immediate: true })
|
||||
|
||||
|
||||
// 移动端菜单状态
|
||||
const mobileMenuOpen = ref(false)
|
||||
|
@ -143,6 +143,7 @@ export const useUserStore = defineStore('user', () => {
|
||||
|
||||
// 强制刷新用户信息
|
||||
const refreshUserInfo = async () => {
|
||||
console.log('🔄 强制刷新用户信息...')
|
||||
return await getCurrentUser(true)
|
||||
}
|
||||
|
||||
@ -181,9 +182,35 @@ export const useUserStore = defineStore('user', () => {
|
||||
user.value = JSON.parse(savedUser)
|
||||
token.value = savedToken
|
||||
|
||||
// 不强制刷新用户信息,避免API超时导致白屏
|
||||
// 如果需要验证token有效性,可以在用户操作时进行
|
||||
console.log('✅ 用户认证状态已恢复')
|
||||
|
||||
// 验证token有效性并获取最新用户信息
|
||||
try {
|
||||
console.log('🔍 验证token有效性并获取最新用户信息...')
|
||||
const userInfoResponse = await AuthApi.getUserInfo()
|
||||
|
||||
if (userInfoResponse.success && userInfoResponse.result) {
|
||||
// 将后端用户信息转换为前端格式
|
||||
const convertedUser = AuthApi.convertBackendUserToUser(userInfoResponse.result)
|
||||
|
||||
// 更新用户信息
|
||||
user.value = convertedUser
|
||||
localStorage.setItem('user', JSON.stringify(convertedUser))
|
||||
|
||||
console.log('✅ 用户信息已更新:', convertedUser)
|
||||
} else {
|
||||
console.warn('⚠️ 获取用户信息失败,保持本地缓存的用户信息')
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.warn('⚠️ 验证token或获取用户信息失败:', error)
|
||||
|
||||
// 如果是401错误,说明token已过期,清除认证状态
|
||||
if (error.response?.status === 401) {
|
||||
console.log('🔄 Token已过期,清除认证状态')
|
||||
await logout()
|
||||
}
|
||||
// 其他错误(如网络错误)保持本地缓存的用户信息
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to parse saved user data:', error)
|
||||
await logout()
|
||||
|
@ -210,6 +210,25 @@ const handleSubmit = async () => {
|
||||
// 显示加载状态
|
||||
userStore.isLoading = true
|
||||
|
||||
if (isRegisterMode.value) {
|
||||
// 注册逻辑
|
||||
await handleRegister()
|
||||
} else {
|
||||
// 登录逻辑
|
||||
await handleLogin()
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('表单提交失败:', error)
|
||||
if (error.message) {
|
||||
message.error(error.message)
|
||||
}
|
||||
} finally {
|
||||
userStore.isLoading = false
|
||||
}
|
||||
}
|
||||
|
||||
// 处理登录
|
||||
const handleLogin = async () => {
|
||||
console.log('🚀 开始登录:', { account: formData.studentId, password: '***' })
|
||||
console.log('🔍 表单密码长度:', formData.password?.length)
|
||||
|
||||
@ -315,25 +334,52 @@ const handleSubmit = async () => {
|
||||
console.error('❌ 登录失败,响应码:', response.code)
|
||||
message.error(response.message || '登录失败')
|
||||
}
|
||||
}
|
||||
|
||||
// 处理注册
|
||||
const handleRegister = async () => {
|
||||
console.log('🚀 开始注册:', { account: formData.studentId, inviteCode: formData.inviteCode })
|
||||
|
||||
try {
|
||||
// 调用注册API
|
||||
const response = await AuthApi.register({
|
||||
username: formData.studentId,
|
||||
email: formData.studentId.includes('@') ? formData.studentId : '',
|
||||
phone: /^[0-9]+$/.test(formData.studentId) ? formData.studentId : '',
|
||||
password: formData.password,
|
||||
confirmPassword: formData.password,
|
||||
captcha: '', // 暂时不需要验证码
|
||||
inviteCode: formData.inviteCode
|
||||
})
|
||||
|
||||
console.log('✅ 注册响应:', response)
|
||||
|
||||
if (response.code === 200 || response.code === 0) {
|
||||
message.success('注册成功!请使用您的账号登录')
|
||||
|
||||
// 注册成功后切换到登录模式
|
||||
isRegisterMode.value = false
|
||||
|
||||
// 清空邀请码,保留账号和密码方便登录
|
||||
formData.inviteCode = ''
|
||||
} else {
|
||||
message.error(response.message || '注册失败')
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('登录失败:', error)
|
||||
console.error('注册失败:', error)
|
||||
|
||||
// 处理不同类型的错误
|
||||
if (error.response?.status === 401) {
|
||||
message.error('邮箱或密码错误')
|
||||
} else if (error.response?.status === 429) {
|
||||
message.error('登录尝试过于频繁,请稍后再试')
|
||||
if (error.response?.status === 400) {
|
||||
message.error('请求参数错误,请检查输入信息')
|
||||
} else if (error.response?.status === 409) {
|
||||
message.error('账号已被注册,请使用其他账号')
|
||||
} else if (error.response?.data?.message) {
|
||||
// 显示后端返回的具体错误信息
|
||||
message.error(error.response.data.message)
|
||||
} else if (error.message) {
|
||||
// 显示错误对象中的消息
|
||||
message.error(error.message)
|
||||
} else {
|
||||
message.error('网络错误,请检查网络连接')
|
||||
}
|
||||
} finally {
|
||||
userStore.isLoading = false
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -398,7 +398,7 @@ const goBack = () => {
|
||||
// 根据当前路由上下文决定跳转路径
|
||||
const currentRoute = route.path;
|
||||
const bankId = route.params.bankId;
|
||||
|
||||
|
||||
if (currentRoute.includes('/course-editor/')) {
|
||||
// 如果在课程编辑器中,返回课程编辑器的题库管理
|
||||
const courseId = route.params.id || route.params.courseId;
|
||||
@ -511,12 +511,15 @@ const createNewQuestion = async (bankId: string) => {
|
||||
try {
|
||||
// 只调用一次API创建题目
|
||||
const questionData = {
|
||||
repoId: bankId, // 题库ID
|
||||
parentId: undefined, // 父题目ID,普通题目为undefined
|
||||
type: getQuestionTypeNumber(questionForm.type),
|
||||
content: questionForm.title,
|
||||
analysis: questionForm.explanation || '',
|
||||
difficulty: getDifficultyNumber(questionForm.difficulty),
|
||||
score: questionForm.score
|
||||
score: questionForm.score,
|
||||
degree: 1, // 程度,默认为1
|
||||
ability: 1 // 能力,默认为1
|
||||
};
|
||||
|
||||
console.log('🚀 创建题目,数据:', questionData);
|
||||
@ -541,31 +544,102 @@ const createNewQuestion = async (bankId: string) => {
|
||||
}
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
throw new Error('创建题目失败');
|
||||
if (!success || !questionId) {
|
||||
throw new Error('创建题目失败:未获取到题目ID');
|
||||
}
|
||||
|
||||
console.log('✅ 题目创建成功,题目ID:', questionId);
|
||||
|
||||
message.success('题目保存成功');
|
||||
|
||||
// 根据当前路由上下文决定跳转路径
|
||||
const currentRoute = route.path;
|
||||
if (currentRoute.includes('/course-editor/')) {
|
||||
// 如果在课程编辑器中,返回课程编辑器的题库管理
|
||||
const courseId = route.params.id || route.params.courseId;
|
||||
router.push(`/teacher/course-editor/${courseId}/question-bank/${bankId}/questions`);
|
||||
} else {
|
||||
// 如果在考试管理中,返回考试管理的题库管理
|
||||
router.push(`/teacher/exam-management/question-bank/${bankId}/questions`);
|
||||
// 如果是选择题或判断题,需要创建选项
|
||||
const questionType = getQuestionTypeNumber(questionForm.type);
|
||||
if (questionType === 0 || questionType === 1 || questionType === 2) { // 单选、多选、判断题
|
||||
await createQuestionOptions(questionId, questionType);
|
||||
}
|
||||
|
||||
message.success('题目创建成功!');
|
||||
router.back();
|
||||
|
||||
} catch (error: any) {
|
||||
console.error('创建题目流程失败:', error);
|
||||
console.error('❌ 创建题目失败:', error);
|
||||
message.error(error.message || '创建题目失败');
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
// 创建题目选项
|
||||
const createQuestionOptions = async (questionId: string, questionType: number) => {
|
||||
try {
|
||||
console.log('🚀 开始创建题目选项,题目ID:', questionId, '题目类型:', questionType);
|
||||
|
||||
let optionsToCreate: Array<{
|
||||
content: string;
|
||||
izCorrent: number;
|
||||
orderNo: number;
|
||||
}> = [];
|
||||
|
||||
if (questionType === 0) { // 单选题
|
||||
// 获取选项数据
|
||||
const options = questionForm.options || [];
|
||||
const correctAnswer = questionForm.correctAnswer;
|
||||
|
||||
optionsToCreate = options.map((option: any, index: number) => ({
|
||||
content: option.content || option.text || '',
|
||||
izCorrent: option.letter === correctAnswer ? 1 : 0,
|
||||
orderNo: index
|
||||
}));
|
||||
|
||||
} else if (questionType === 1) { // 多选题
|
||||
const options = questionForm.options || [];
|
||||
const correctAnswers = questionForm.correctAnswers || [];
|
||||
|
||||
optionsToCreate = options.map((option: any, index: number) => ({
|
||||
content: option.content || option.text || '',
|
||||
izCorrent: correctAnswers.includes(option.letter) ? 1 : 0,
|
||||
orderNo: index
|
||||
}));
|
||||
|
||||
} else if (questionType === 2) { // 判断题
|
||||
// 判断题固定两个选项:正确和错误
|
||||
const correctAnswer = questionForm.correctAnswer;
|
||||
const isCorrectTrue = String(correctAnswer) === 'true' || correctAnswer === 1;
|
||||
optionsToCreate = [
|
||||
{
|
||||
content: '正确',
|
||||
izCorrent: isCorrectTrue ? 1 : 0,
|
||||
orderNo: 0
|
||||
},
|
||||
{
|
||||
content: '错误',
|
||||
izCorrent: isCorrectTrue ? 0 : 1,
|
||||
orderNo: 1
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
console.log('📊 准备创建的选项:', optionsToCreate);
|
||||
|
||||
// 批量创建选项
|
||||
for (const option of optionsToCreate) {
|
||||
const optionData = {
|
||||
questionId: questionId,
|
||||
content: option.content,
|
||||
izCorrent: option.izCorrent,
|
||||
orderNo: option.orderNo
|
||||
};
|
||||
|
||||
console.log('🚀 创建选项:', optionData);
|
||||
const optionResponse = await ExamApi.createQuestionOption(optionData);
|
||||
console.log('✅ 选项创建成功:', optionResponse);
|
||||
}
|
||||
|
||||
console.log('✅ 所有选项创建完成');
|
||||
|
||||
} catch (error: any) {
|
||||
console.error('❌ 创建题目选项失败:', error);
|
||||
throw new Error('创建题目选项失败:' + (error.message || '未知错误'));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
// 验证答案设置
|
||||
|
Loading…
x
Reference in New Issue
Block a user