用户信息变更

This commit is contained in:
2025-11-26 12:03:34 +08:00
parent 8ff849d050
commit 8d8ecf8763
15 changed files with 203 additions and 88 deletions

View File

@@ -1,6 +1,6 @@
-- 修复字符编码问题 -- 修复字符编码问题
-- 删除现有数据库(如果存在) -- 删除现有数据库(如果存在)
-- DROP DATABASE IF EXISTS `school_news`; DROP DATABASE IF EXISTS `school_news`;
-- 创建数据库使用utf8mb4字符集 -- 创建数据库使用utf8mb4字符集
CREATE DATABASE IF NOT EXISTS `school_news` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE DATABASE IF NOT EXISTS `school_news` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

View File

@@ -32,6 +32,7 @@ CREATE TABLE `tb_sys_user_info` (
`given_name` VARCHAR(50) DEFAULT NULL COMMENT '', `given_name` VARCHAR(50) DEFAULT NULL COMMENT '',
`full_name` VARCHAR(100) DEFAULT NULL COMMENT '全名', `full_name` VARCHAR(100) DEFAULT NULL COMMENT '全名',
`level` INT(4) DEFAULT 1 COMMENT '等级', `level` INT(4) DEFAULT 1 COMMENT '等级',
`student_id` VARCHAR(50) DEFAULT NULL COMMENT '学号',
`id_card` VARCHAR(50) DEFAULT NULL COMMENT '身份证号', `id_card` VARCHAR(50) DEFAULT NULL COMMENT '身份证号',
`address` VARCHAR(255) DEFAULT NULL COMMENT '地址', `address` VARCHAR(255) DEFAULT NULL COMMENT '地址',
`create_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `create_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',

View File

@@ -62,6 +62,13 @@ public class TbSysUserInfo extends BaseDTO {
*/ */
private Float level; private Float level;
/**
* @description 学号
* @author yslg
* @since 2025-11-26
*/
private String studentId;
/** /**
* @description 身份证号 * @description 身份证号
* @author yslg * @author yslg
@@ -132,6 +139,14 @@ public class TbSysUserInfo extends BaseDTO {
this.level = level; this.level = level;
} }
public String getStudentId() {
return studentId;
}
public void setStudentId(String studentId) {
this.studentId = studentId;
}
public String getIdCard() { public String getIdCard() {
return idCard; return idCard;
} }
@@ -160,6 +175,7 @@ public class TbSysUserInfo extends BaseDTO {
", givenName='" + givenName + '\'' + ", givenName='" + givenName + '\'' +
", fullName='" + fullName + '\'' + ", fullName='" + fullName + '\'' +
", level=" + level + ", level=" + level +
", studentId='" + studentId + '\'' +
", idCard='" + idCard + '\'' + ", idCard='" + idCard + '\'' +
", address='" + address + '\'' + ", address='" + address + '\'' +
", createTime=" + getCreateTime() + ", createTime=" + getCreateTime() +

View File

@@ -16,6 +16,7 @@ public class UserVO {
private String givenName; private String givenName;
private String fullName; private String fullName;
private Float level; private Float level;
private String studentId;
private String idCard; private String idCard;
private String address; private String address;
private String deptID; private String deptID;
@@ -138,6 +139,12 @@ public class UserVO {
public void setLevel(Float level) { public void setLevel(Float level) {
this.level = level; this.level = level;
} }
public String getStudentId() {
return studentId;
}
public void setStudentId(String studentId) {
this.studentId = studentId;
}
public Date getCreateTime() { public Date getCreateTime() {
return createTime; return createTime;
} }

View File

@@ -611,12 +611,12 @@ public class SysUserServiceImpl implements SysUserService {
filter.setEmail(email); filter.setEmail(email);
filter.setDeleted(false); filter.setDeleted(false);
if (StringUtils.hasText(excludeId)) { // if (StringUtils.hasText(excludeId)) {
filter.setID(excludeId); // filter.setID(excludeId);
} // }
long count = userMapper.selectByFilter(filter, userDeptRoles).size(); List<TbSysUser> userList = userMapper.selectByFilter(filter, userDeptRoles);
boolean exists = count > 0; boolean exists = userList.size() >1 && (userList.size() == 1 && !userList.get(0).getID().equals(excludeId));
logger.info("邮箱存在性检查完成:{},存在:{}", email, exists); logger.info("邮箱存在性检查完成:{},存在:{}", email, exists);
resultDomain.success("检查完成", exists); resultDomain.success("检查完成", exists);

View File

@@ -10,6 +10,7 @@
<result column="given_name" property="givenName" /> <result column="given_name" property="givenName" />
<result column="full_name" property="fullName" /> <result column="full_name" property="fullName" />
<result column="level" property="level" /> <result column="level" property="level" />
<result column="student_id" property="studentId" />
<result column="id_card" property="idCard" /> <result column="id_card" property="idCard" />
<result column="address" property="address" /> <result column="address" property="address" />
<result column="create_time" property="createTime" /> <result column="create_time" property="createTime" />
@@ -19,7 +20,7 @@
</resultMap> </resultMap>
<sql id="Base_Column_List"> <sql id="Base_Column_List">
id, user_id, avatar, gender, family_name, given_name, full_name, level, id_card, address, create_time, update_time, delete_time, deleted id, user_id, avatar, gender, student_id, family_name, given_name, full_name, level, id_card, address, create_time, update_time, delete_time, deleted
</sql> </sql>
<!-- insertUserInfo --> <!-- insertUserInfo -->
@@ -39,6 +40,7 @@
<if test="userInfo.givenName != null">given_name = #{userInfo.givenName},</if> <if test="userInfo.givenName != null">given_name = #{userInfo.givenName},</if>
<if test="userInfo.fullName != null">full_name = #{userInfo.fullName},</if> <if test="userInfo.fullName != null">full_name = #{userInfo.fullName},</if>
<if test="userInfo.level != null">level = #{userInfo.level},</if> <if test="userInfo.level != null">level = #{userInfo.level},</if>
<if test="userInfo.studentId != null">student_id = #{userInfo.studentId},</if>
<if test="userInfo.idCard != null">id_card = #{userInfo.idCard},</if> <if test="userInfo.idCard != null">id_card = #{userInfo.idCard},</if>
<if test="userInfo.address != null">address = #{userInfo.address},</if> <if test="userInfo.address != null">address = #{userInfo.address},</if>
<if test="userInfo.updater != null">updater = #{userInfo.updater},</if> <if test="userInfo.updater != null">updater = #{userInfo.updater},</if>

View File

@@ -50,6 +50,7 @@
<result column="dept_name" property="deptName" jdbcType="VARCHAR"/> <result column="dept_name" property="deptName" jdbcType="VARCHAR"/>
<result column="role_name" property="roleName" jdbcType="VARCHAR"/> <result column="role_name" property="roleName" jdbcType="VARCHAR"/>
<result column="level" property="level" jdbcType="INTEGER"/> <result column="level" property="level" jdbcType="INTEGER"/>
<result column="student_id" property="studentId" jdbcType="VARCHAR"/>
<result column="id_card" property="idCard" jdbcType="VARCHAR"/> <result column="id_card" property="idCard" jdbcType="VARCHAR"/>
<result column="address" property="address" jdbcType="VARCHAR"/> <result column="address" property="address" jdbcType="VARCHAR"/>
<result column="create_time" property="createTime" jdbcType="TIMESTAMP"/> <result column="create_time" property="createTime" jdbcType="TIMESTAMP"/>
@@ -64,13 +65,13 @@
</sql> </sql>
<sql id="FullUser_Column_List"> <sql id="FullUser_Column_List">
id, user_id, avatar, gender, family_name, given_name, full_name, id_card, address, id, user_id, avatar, gender, family_name, given_name, full_name, student_id,id_card, address,
create_time, update_time, delete_time, deleted, status create_time, update_time, delete_time, deleted, status
</sql> </sql>
<!-- 用户信息字段 --> <!-- 用户信息字段 -->
<sql id="UserInfo_Column_List"> <sql id="UserInfo_Column_List">
id, user_id, avatar, gender, family_name, given_name, full_name, id_card, address, id, user_id, avatar, gender, family_name, given_name, full_name, student_id,id_card, address,
create_time, update_time, delete_time, deleted create_time, update_time, delete_time, deleted
</sql> </sql>
@@ -230,6 +231,7 @@
ui.given_name, ui.given_name,
ui.full_name, ui.full_name,
ui.level, ui.level,
ui.student_id,
ui.id_card, ui.id_card,
ui.address, ui.address,
udr.dept_id, udr.dept_id,
@@ -349,6 +351,7 @@
ui.given_name, ui.given_name,
ui.full_name, ui.full_name,
ui.level, ui.level,
ui.student_id,
ui.id_card, ui.id_card,
ui.address, ui.address,
udr.dept_id, udr.dept_id,
@@ -491,6 +494,8 @@
<if test="userInfo.familyName != null">family_name = #{userInfo.familyName},</if> <if test="userInfo.familyName != null">family_name = #{userInfo.familyName},</if>
<if test="userInfo.givenName != null">given_name = #{userInfo.givenName},</if> <if test="userInfo.givenName != null">given_name = #{userInfo.givenName},</if>
<if test="userInfo.fullName != null">full_name = #{userInfo.fullName},</if> <if test="userInfo.fullName != null">full_name = #{userInfo.fullName},</if>
<if test="userInfo.level != null">level = #{userInfo.level},</if>
<if test="userInfo.studentId != null">student_id = #{userInfo.studentId},</if>
<if test="userInfo.idCard != null">id_card = #{userInfo.idCard},</if> <if test="userInfo.idCard != null">id_card = #{userInfo.idCard},</if>
<if test="userInfo.address != null">address = #{userInfo.address},</if> <if test="userInfo.address != null">address = #{userInfo.address},</if>
<if test="userInfo.updateTime != null">update_time = #{userInfo.updateTime}</if> <if test="userInfo.updateTime != null">update_time = #{userInfo.updateTime}</if>
@@ -562,6 +567,7 @@
tus.email, tus.email,
tsui.avatar, tsui.avatar,
tsui.gender, tsui.gender,
tsui.student_id,
(SELECT dept_path FROM dept_hierarchy WHERE parent_id IS NULL LIMIT 1) as dept_name, (SELECT dept_path FROM dept_hierarchy WHERE parent_id IS NULL LIMIT 1) as dept_name,
tsr.name as role_name, tsr.name as role_name,
tsui.level, tsui.level,

View File

@@ -56,15 +56,34 @@ public class UserProfileController {
* 更新个人信息 * 更新个人信息
*/ */
@PutMapping("/info/update") @PutMapping("/info/update")
public ResultDomain<TbSysUserInfo> updateUserProfile(@HttpLogin LoginDomain loginDomain, @RequestBody TbSysUserInfo userInfo) { public ResultDomain<UserVO> updateUserProfile(@HttpLogin LoginDomain loginDomain, @RequestBody UserVO userVO) {
// TODO: 实现更新个人信息(姓名、部门、联系方式等) ResultDomain<UserVO> result = new ResultDomain<>();
ResultDomain<TbSysUserInfo> result = userService.updateUserInfo(userInfo); // 更新tb_sys_user
if (result.isSuccess()) { TbSysUser user = new TbSysUser();
user.setID(userVO.getUserID());
user.setUsername(userVO.getUsername());
user.setEmail(userVO.getEmail());
user.setPhone(userVO.getPhone());
ResultDomain<TbSysUser> re = userService.updateUser(user);
if (!re.isSuccess()) {
result.fail(re.getMessage());
return result;
}
// 更新tb_sys_user_info
TbSysUserInfo userInfo = new TbSysUserInfo();
userInfo.setUserID(userVO.getUserID());
userInfo.setAvatar(userVO.getAvatar());
userInfo.setGender(userVO.getGender());
userInfo.setStudentId(userVO.getStudentId());
ResultDomain<TbSysUserInfo> result1 = userService.updateUserInfo(userInfo);
if (result1.isSuccess()) {
result.success("个人信息更新成功", userVO);
return result; return result;
} else { } else {
ResultDomain<TbSysUserInfo> result2 = new ResultDomain<>(); result.fail(result1.getCode(), result1.getMessage());
result2.fail(result.getCode(), result.getMessage()); return result;
return result2;
} }
} }

View File

@@ -184,7 +184,7 @@ request.interceptors.response.use(
case 401: case 401:
ElMessage.error('认证失败,请重新登录'); ElMessage.error('认证失败,请重新登录');
TokenManager.removeToken(); TokenManager.removeToken();
window.location.href = '/login'; window.location.href = '/schoolNewsWeb/login';
break; break;
case 403: case 403:
ElMessage.error('没有权限访问该资源'); ElMessage.error('没有权限访问该资源');

View File

@@ -31,7 +31,7 @@ export const userProfileApi = {
* @param userInfo 用户信息 * @param userInfo 用户信息
* @returns Promise<ResultDomain<any>> * @returns Promise<ResultDomain<any>>
*/ */
async updateUserProfile(userInfo: any): Promise<ResultDomain<any>> { async updateUserProfile(userInfo: UserVO): Promise<ResultDomain<any>> {
const response = await api.put<any>('/usercenter/profile/info/update', userInfo); const response = await api.put<any>('/usercenter/profile/info/update', userInfo);
return response.data; return response.data;
}, },

View File

@@ -89,6 +89,7 @@ import type { UserVO, SysMenu } from '@/types';
import { ChangeHome } from '@/components/base'; import { ChangeHome } from '@/components/base';
import userIcon from '@/assets/imgs/user.svg'; import userIcon from '@/assets/imgs/user.svg';
import settingsIcon from '@/assets/imgs/settings.svg'; import settingsIcon from '@/assets/imgs/settings.svg';
import { FILE_DOWNLOAD_URL } from '@/config';
// Props // Props
interface Props { interface Props {
user?: UserVO | null; user?: UserVO | null;
@@ -118,7 +119,7 @@ const isLoggedIn = computed(() => {
}); });
const userAvatar = computed(() => { const userAvatar = computed(() => {
return props.user?.avatar || ''; return props.user?.avatar ? FILE_DOWNLOAD_URL + props.user?.avatar : '';
}); });
const avatarText = computed(() => { const avatarText = computed(() => {

View File

@@ -29,6 +29,9 @@ export interface SysUser extends BaseDTO {
export interface SysUserInfo extends BaseDTO { export interface SysUserInfo extends BaseDTO {
/** 用户ID */ /** 用户ID */
userID?: string; userID?: string;
/** 用户名迁移到userInfo中便于统一展示 */
username?: string;
studentId?: string;
/** 真实姓名 */ /** 真实姓名 */
realName?: string; realName?: string;
/** 昵称 */ /** 昵称 */
@@ -51,6 +54,7 @@ export interface SysUserInfo extends BaseDTO {
export interface UserVO extends BaseDTO { export interface UserVO extends BaseDTO {
/** 用户ID兼容字段 */ /** 用户ID兼容字段 */
userID?: string; userID?: string;
studentId?: string;
/** 用户名 */ /** 用户名 */
username?: string; username?: string;
/** 邮箱 */ /** 邮箱 */

View File

@@ -13,7 +13,7 @@
<!-- 头像 --> <!-- 头像 -->
<div class="avatar-wrapper"> <div class="avatar-wrapper">
<img <img
:src="userInfo?.avatar && userInfo.avatar!='default' ? userInfo.avatar : defaultAvatar" :src="userInfo?.avatar && userInfo.avatar!='default' ? FILE_DOWNLOAD_URL + userInfo.avatar : defaultAvatar"
:alt="userInfo?.username" :alt="userInfo?.username"
class="avatar" class="avatar"
/> />
@@ -57,6 +57,7 @@ import femaleIcon from '@/assets/imgs/female.svg';
import { ElMessage } from 'element-plus'; import { ElMessage } from 'element-plus';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { getLevelWordIconUrl } from '@/utils/iconUtils'; import { getLevelWordIconUrl } from '@/utils/iconUtils';
import { FILE_DOWNLOAD_URL } from '@/config';
const router = useRouter(); const router = useRouter();
const userInfo = ref<UserVO>(); const userInfo = ref<UserVO>();

View File

@@ -1,20 +1,21 @@
<template> <template>
<div class="personal-info"> <div class="personal-info">
<el-form :model="userForm" label-width="120px" class="info-form"> <el-form :model="userForm" label-width="120px" class="info-form" v-loading="loading">
<el-form-item label="头像"> <el-form-item label="头像">
<div class="avatar-upload"> <FileUpload
<img :src="userForm.avatar || defaultAvatar" alt="头像" class="avatar-preview" /> :as-dialog="false"
<el-button size="small" @click="handleAvatarUpload">更换头像</el-button> list-type="cover"
</div> v-model:cover-url="userForm.avatar"
accept="image/*"
:max-size="5"
tip="点击上传头像"
module="avatar"
@success="handleAvatarSuccess"
/>
</el-form-item> </el-form-item>
<el-form-item label="用户名"> <el-form-item label="用户名">
<el-input v-model="userForm.username" disabled /> <el-input v-model="userForm.username" />
</el-form-item>
<el-form-item label="姓名">
<el-input v-model="userForm.realName" />
</el-form-item> </el-form-item>
<el-form-item label="性别"> <el-form-item label="性别">
@@ -36,71 +37,142 @@
<el-input v-model="userForm.deptName" disabled /> <el-input v-model="userForm.deptName" disabled />
</el-form-item> </el-form-item>
<el-form-item label="个人简介"> <el-form-item label="学号">
<el-input v-model="userForm.bio" type="textarea" :rows="4" placeholder="介绍一下自己吧..." /> <el-input v-model="userForm.studentId" placeholder="请输入学号" />
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" @click="handleSave">保存</el-button> <el-button type="primary" @click="handleSave" :loading="saving">保存</el-button>
<el-button @click="handleCancel">取消</el-button> <el-button @click="handleCancel">取消</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted } from 'vue'; import { ref, onMounted } from 'vue';
import { ElForm, ElFormItem, ElInput, ElButton, ElRadio, ElRadioGroup, ElMessage } from 'element-plus'; import { ElMessage } from 'element-plus';
import { UserCenterLayout } from '@/views/user/user-center'; import { userProfileApi } from '@/apis/usercenter';
// 默认头像 import { FILE_DOWNLOAD_URL } from '@/config';
const defaultAvatar = new URL('@/assets/imgs/default-avatar.png', import.meta.url).href; import { FileUpload } from '@/components/file';
import type { SysFile, UserVO } from '@/types';
const loading = ref(false);
const saving = ref(false);
const originalUserInfo = ref<any>(null);
const userForm = ref({ const userForm = ref({
avatar: '', avatar: '',
username: '', username: '',
realName: '',
gender: 1, gender: 1,
phone: '', phone: '',
email: '', email: '',
deptName: '', deptName: '',
bio: '' studentId: ''
}); });
onMounted(() => { // 接口返回的用户ID用于提交更新
// TODO: 加载用户信息 const profileUserId = ref<string | undefined>(undefined);
loadUserInfo();
});
function loadUserInfo() { // 加载用户信息
// 模拟数据 async function loadUserInfo() {
userForm.value = { loading.value = true;
avatar: '', try {
username: '平台用户bc7a1b', const result = await userProfileApi.getUserProfile();
realName: '张三', if (result.code === 200 && result.data) {
gender: 1, const info: UserVO = result.data;
phone: '15268425987', profileUserId.value = info.userID;
email: 'zhangsan@example.com',
deptName: '机械学院', // 保存原始数据用于取消操作
bio: '' let avatarVal = info.avatar || '';
if (avatarVal === 'default') {
avatarVal = '';
} else if (avatarVal && FILE_DOWNLOAD_URL && avatarVal.startsWith(FILE_DOWNLOAD_URL)) {
avatarVal = avatarVal.substring(FILE_DOWNLOAD_URL.length);
}
originalUserInfo.value = {
avatar: avatarVal,
username: info.username || '',
gender: info.gender ?? 1,
phone: info.phone || '',
email: info.email || '',
deptName: info.deptName || '',
studentId: info.studentId || ''
}; };
// 填充表单
userForm.value = { ...originalUserInfo.value };
} else {
ElMessage.error(result.message || '获取个人信息失败');
}
} catch (error: any) {
console.error('加载用户信息失败:', error);
ElMessage.error('加载用户信息失败: ' + (error.message || '未知错误'));
} finally {
loading.value = false;
}
} }
function handleAvatarUpload() { /**
// TODO: 上传头像 * 头像上传成功回调
ElMessage.info('上传头像功能开发中'); */
function handleAvatarSuccess(files: SysFile[]) {
if (files && files.length > 0) {
ElMessage.success('头像上传成功');
}
} }
function handleSave() { /**
// TODO: 保存用户信息 * 保存用户信息
*/
async function handleSave() {
if (!profileUserId.value) {
ElMessage.warning('未获取到用户ID');
}
saving.value = true;
try {
const userInfo: UserVO = {
userID: profileUserId.value,
avatar: userForm.value.avatar,
gender: userForm.value.gender,
studentId: userForm.value.studentId,
username: userForm.value.username,
phone: userForm.value.phone,
email: userForm.value.email,
};
const result = await userProfileApi.updateUserProfile(userInfo);
if (result.code === 200) {
ElMessage.success('保存成功'); ElMessage.success('保存成功');
await loadUserInfo();
originalUserInfo.value = { ...userForm.value };
} else {
ElMessage.error(result.message || '保存失败');
}
} catch (error: any) {
console.error('保存用户信息失败:', error);
ElMessage.error('保存失败: ' + (error.message || '未知错误'));
} finally {
saving.value = false;
}
} }
/**
* 取消编辑
*/
function handleCancel() { function handleCancel() {
// 重置表单 if (originalUserInfo.value) {
loadUserInfo(); userForm.value = { ...originalUserInfo.value };
ElMessage.info('已取消修改');
}
} }
onMounted(() => {
loadUserInfo();
});
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@@ -111,18 +183,4 @@ function handleCancel() {
.info-form { .info-form {
padding: 20px 0; padding: 20px 0;
} }
.avatar-upload {
display: flex;
align-items: center;
gap: 20px;
}
.avatar-preview {
width: 80px;
height: 80px;
border-radius: 50%;
object-fit: cover;
border: 2px solid #e0e0e0;
}
</style> </style>