修复用户和题目管理问题

This commit is contained in:
xiao12feng 2025-11-20 20:42:39 +08:00
parent d729451133
commit 23fe845e7d
16 changed files with 635 additions and 119 deletions

View File

@ -1,6 +1,7 @@
package com.ddnai.web.controller.psychology; package com.ddnai.web.controller.psychology;
import java.util.List; import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
@ -12,6 +13,8 @@ import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.ddnai.common.annotation.Anonymous;
import com.ddnai.common.annotation.Log; import com.ddnai.common.annotation.Log;
import com.ddnai.common.core.controller.BaseController; import com.ddnai.common.core.controller.BaseController;
import com.ddnai.common.core.domain.AjaxResult; import com.ddnai.common.core.domain.AjaxResult;
@ -20,6 +23,7 @@ import com.ddnai.common.core.page.TableDataInfo;
import com.ddnai.common.enums.BusinessType; import com.ddnai.common.enums.BusinessType;
import com.ddnai.common.utils.SecurityUtils; import com.ddnai.common.utils.SecurityUtils;
import com.ddnai.common.utils.StringUtils; import com.ddnai.common.utils.StringUtils;
import com.ddnai.common.utils.poi.ExcelUtil;
import com.ddnai.system.domain.psychology.PsyUserProfile; import com.ddnai.system.domain.psychology.PsyUserProfile;
import com.ddnai.system.service.ISysDeptService; import com.ddnai.system.service.ISysDeptService;
import com.ddnai.system.service.ISysPostService; import com.ddnai.system.service.ISysPostService;
@ -235,4 +239,45 @@ public class PsyUserProfileController extends BaseController
{ {
return toAjax(profileService.deleteProfileByIds(profileIds)); return toAjax(profileService.deleteProfileByIds(profileIds));
} }
// ==================== 导入导出功能 ====================
/**
* 导出用户档案列表
*/
@PreAuthorize("@ss.hasPermi('psychology:profile:export')")
@Log(title = "用户档案", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, PsyUserProfile profile)
{
List<PsyUserProfile> list = profileService.selectProfileList(profile);
ExcelUtil<PsyUserProfile> util = new ExcelUtil<PsyUserProfile>(PsyUserProfile.class);
util.exportExcel(response, list, "用户档案数据");
}
/**
* 下载用户档案导入模板
*/
@Anonymous
@GetMapping("/importTemplate")
public void importTemplate(HttpServletResponse response)
{
ExcelUtil<PsyUserProfile> util = new ExcelUtil<PsyUserProfile>(PsyUserProfile.class);
util.importTemplateExcel(response, "用户档案数据");
}
/**
* 导入用户档案数据
*/
@PreAuthorize("@ss.hasPermi('psychology:profile:import')")
@Log(title = "用户档案", businessType = BusinessType.IMPORT)
@PostMapping("/importData")
public AjaxResult importData(MultipartFile file, boolean updateSupport) throws Exception
{
ExcelUtil<PsyUserProfile> util = new ExcelUtil<PsyUserProfile>(PsyUserProfile.class);
List<PsyUserProfile> profileList = util.importExcel(file.getInputStream());
String operName = getUsername();
String message = profileService.importProfile(profileList, updateSupport, operName);
return success(message);
}
} }

View File

@ -178,34 +178,23 @@ public class SysLoginController
String infoNumberTrimmed = infoNumber.trim(); String infoNumberTrimmed = infoNumber.trim();
SysUser user = null; SysUser user = null;
// 优先通过信息编号查找用户档案然后通过userId查找用户 // 严格模式必须通过信息编号在用户档案中查找
// 只有在用户档案中存在的信息编号才能登录
com.ddnai.system.domain.psychology.PsyUserProfile profile = userProfileService.selectProfileByInfoNumber(infoNumberTrimmed); com.ddnai.system.domain.psychology.PsyUserProfile profile = userProfileService.selectProfileByInfoNumber(infoNumberTrimmed);
if (profile != null && profile.getUserId() != null)
if (profile == null)
{ {
user = userService.selectUserById(profile.getUserId()); return AjaxResult.error("信息编号不存在,请先创建用户档案");
} }
// 如果通过信息编号找不到则使用原来的逻辑兼容旧版本 if (profile.getUserId() == null)
if (user == null)
{ {
// 支持两种方式1. 通过user_name查找 2. 如果是数字通过user_id查找 return AjaxResult.error("用户档案异常,未关联系统用户");
user = userService.selectUserByUserName(infoNumberTrimmed);
// 如果通过user_name找不到且输入的是数字尝试通过user_id查找
if (user == null && StringUtils.isNumeric(infoNumberTrimmed))
{
try
{
Long userId = Long.parseLong(infoNumberTrimmed);
user = userService.selectUserById(userId);
}
catch (NumberFormatException e)
{
// 数字格式错误忽略
}
}
} }
// 通过档案中的userId查找用户
user = userService.selectUserById(profile.getUserId());
// 检查系统有效时间系统管理员不受限制 // 检查系统有效时间系统管理员不受限制
try try
{ {

View File

@ -16,6 +16,7 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import com.ddnai.common.annotation.Anonymous;
import com.ddnai.common.annotation.Log; import com.ddnai.common.annotation.Log;
import com.ddnai.common.core.controller.BaseController; import com.ddnai.common.core.controller.BaseController;
import com.ddnai.common.core.domain.AjaxResult; import com.ddnai.common.core.domain.AjaxResult;
@ -87,7 +88,8 @@ public class SysUserController extends BaseController
return success(message); return success(message);
} }
@PostMapping("/importTemplate") @Anonymous
@GetMapping("/importTemplate")
public void importTemplate(HttpServletResponse response) public void importTemplate(HttpServletResponse response)
{ {
ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class); ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);

View File

@ -21,7 +21,7 @@ public class SysUser extends BaseEntity
{ {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** 用户ID */ /** 用户ID - 只在导出时显示,导入时不需要,通过信息编号自动设置 */
@Excel(name = "用户序号", type = Type.EXPORT, cellType = ColumnType.NUMERIC, prompt = "用户编号") @Excel(name = "用户序号", type = Type.EXPORT, cellType = ColumnType.NUMERIC, prompt = "用户编号")
private Long userId; private Long userId;
@ -92,6 +92,44 @@ public class SysUser extends BaseEntity
/** 角色ID */ /** 角色ID */
private Long roleId; private Long roleId;
// ==================== 心理档案相关字段 ====================
/** 信息编号 */
@Excel(name = "信息编号")
private String infoNumber;
/** 档案类型 */
@Excel(name = "档案类型", readConverterExp = "standard=标准,child=儿童,adult=成人,senior=老年")
private String profileType;
/** 身份证号 */
@Excel(name = "身份证号")
private String idCard;
/** 生日 */
@Excel(name = "生日", width = 30, dateFormat = "yyyy-MM-dd")
private Date birthday;
/** 学历 */
@Excel(name = "学历")
private String education;
/** 职业 */
@Excel(name = "职业")
private String occupation;
/** 地址 */
@Excel(name = "地址")
private String address;
/** 紧急联系人 */
@Excel(name = "紧急联系人")
private String emergencyContact;
/** 紧急联系电话 */
@Excel(name = "紧急联系电话")
private String emergencyPhone;
public SysUser() public SysUser()
{ {
@ -310,6 +348,98 @@ public class SysUser extends BaseEntity
this.roleId = roleId; this.roleId = roleId;
} }
// ==================== 心理档案字段的 Getter/Setter ====================
public String getInfoNumber()
{
return infoNumber;
}
public void setInfoNumber(String infoNumber)
{
this.infoNumber = infoNumber;
}
public String getProfileType()
{
return profileType;
}
public void setProfileType(String profileType)
{
this.profileType = profileType;
}
public String getIdCard()
{
return idCard;
}
public void setIdCard(String idCard)
{
this.idCard = idCard;
}
public Date getBirthday()
{
return birthday;
}
public void setBirthday(Date birthday)
{
this.birthday = birthday;
}
public String getEducation()
{
return education;
}
public void setEducation(String education)
{
this.education = education;
}
public String getOccupation()
{
return occupation;
}
public void setOccupation(String occupation)
{
this.occupation = occupation;
}
public String getAddress()
{
return address;
}
public void setAddress(String address)
{
this.address = address;
}
public String getEmergencyContact()
{
return emergencyContact;
}
public void setEmergencyContact(String emergencyContact)
{
this.emergencyContact = emergencyContact;
}
public String getEmergencyPhone()
{
return emergencyPhone;
}
public void setEmergencyPhone(String emergencyPhone)
{
this.emergencyPhone = emergencyPhone;
}
@Override @Override
public String toString() { public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)

View File

@ -29,8 +29,10 @@ import com.ddnai.framework.manager.AsyncManager;
import com.ddnai.framework.manager.factory.AsyncFactory; import com.ddnai.framework.manager.factory.AsyncFactory;
import com.ddnai.framework.security.context.AuthenticationContextHolder; import com.ddnai.framework.security.context.AuthenticationContextHolder;
import com.ddnai.common.core.domain.entity.SysUser; import com.ddnai.common.core.domain.entity.SysUser;
import com.ddnai.common.core.domain.entity.SysRole;
import com.ddnai.system.service.ISysConfigService; import com.ddnai.system.service.ISysConfigService;
import com.ddnai.system.service.ISysUserService; import com.ddnai.system.service.ISysUserService;
import com.ddnai.system.service.psychology.IPsyUserProfileService;
/** /**
* 登录校验方法 * 登录校验方法
@ -42,6 +44,9 @@ public class SysLoginService
{ {
private static final Logger log = LoggerFactory.getLogger(SysLoginService.class); private static final Logger log = LoggerFactory.getLogger(SysLoginService.class);
@Autowired
private IPsyUserProfileService userProfileService;
@Autowired @Autowired
private TokenService tokenService; private TokenService tokenService;
@ -100,6 +105,36 @@ public class SysLoginService
} }
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"))); AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
LoginUser loginUser = (LoginUser) authentication.getPrincipal(); LoginUser loginUser = (LoginUser) authentication.getPrincipal();
// 检查学员用户是否有档案
SysUser user = loginUser.getUser();
if (user != null && user.getRoles() != null)
{
boolean isStudent = false;
for (SysRole role : user.getRoles())
{
if (role != null && ("student".equalsIgnoreCase(role.getRoleKey()) ||
(role.getRoleName() != null && role.getRoleName().contains("学员"))))
{
isStudent = true;
break;
}
}
// 如果是学员角色必须有档案才能登录
if (isStudent)
{
com.ddnai.system.domain.psychology.PsyUserProfile profile =
userProfileService.selectProfileByUserId(user.getUserId());
if (profile == null || StringUtils.isEmpty(profile.getInfoNumber()))
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, "学员账号必须有用户档案才能登录"));
throw new ServiceException("该账号未创建用户档案,请联系管理员创建档案后再登录");
}
log.info("学员登录验证通过,用户: {}, 信息编号: {}", username, profile.getInfoNumber());
}
}
recordLoginInfo(loginUser.getUserId()); recordLoginInfo(loginUser.getUserId());
// 生成token // 生成token
return tokenService.createToken(loginUser); return tokenService.createToken(loginUser);

View File

@ -1,5 +1,6 @@
package com.ddnai.system.domain.psychology; package com.ddnai.system.domain.psychology;
import com.ddnai.common.annotation.Excel;
import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonFormat;
import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle; import org.apache.commons.lang3.builder.ToStringStyle;
@ -17,44 +18,54 @@ public class PsyUserProfile extends BaseEntity
/** 档案ID */ /** 档案ID */
private Long profileId; private Long profileId;
/** 用户ID关联sys_user */ /** 用户ID关联sys_user - 不在Excel模板中显示通过信息编号自动设置 */
private Long userId; private Long userId;
/** 档案类型standard标准 child儿童 adult成人 senior老年 */ /** 档案类型standard标准 child儿童 adult成人 senior老年 */
@Excel(name = "档案类型", readConverterExp = "standard=标准,child=儿童,adult=成人,senior=老年")
private String profileType; private String profileType;
/** 头像 */ /** 头像 */
private String avatar; private String avatar;
/** 身份证号 */ /** 身份证号 */
@Excel(name = "身份证号")
private String idCard; private String idCard;
/** 档案数据JSON格式 */ /** 档案数据JSON格式 */
private String profileData; private String profileData;
/** 姓名 */ /** 姓名 */
@Excel(name = "姓名")
private String userName; private String userName;
/** 电话 */ /** 电话 */
@Excel(name = "电话")
private String phone; private String phone;
/** 生日 */ /** 生日 */
@JsonFormat(pattern = "yyyy-MM-dd") @JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "生日", width = 30, dateFormat = "yyyy-MM-dd")
private java.util.Date birthday; private java.util.Date birthday;
/** 学历 */ /** 学历 */
@Excel(name = "学历")
private String education; private String education;
/** 职业 */ /** 职业 */
@Excel(name = "职业")
private String occupation; private String occupation;
/** 地址 */ /** 地址 */
@Excel(name = "地址")
private String address; private String address;
/** 紧急联系人 */ /** 紧急联系人 */
@Excel(name = "紧急联系人")
private String emergencyContact; private String emergencyContact;
/** 紧急联系电话 */ /** 紧急联系电话 */
@Excel(name = "紧急联系电话")
private String emergencyPhone; private String emergencyPhone;
/** 病史 */ /** 病史 */
@ -70,6 +81,7 @@ public class PsyUserProfile extends BaseEntity
private String deptName; private String deptName;
/** 信息编号 */ /** 信息编号 */
@Excel(name = "信息编号")
private String infoNumber; private String infoNumber;
public Long getProfileId() public Long getProfileId()

View File

@ -517,6 +517,20 @@ public class SysUserServiceImpl implements ISysUserService
{ {
try try
{ {
// 如果有信息编号使用信息编号作为登录账号
if (StringUtils.isNotEmpty(user.getInfoNumber()))
{
user.setUserName(user.getInfoNumber()); // 登录账号 = 信息编号
}
// 如果没有用户名跳过
if (StringUtils.isEmpty(user.getUserName()))
{
failureNum++;
failureMsg.append("<br/>").append(failureNum).append("、用户名或信息编号不能为空");
continue;
}
// 验证是否存在这个用户 // 验证是否存在这个用户
SysUser u = userMapper.selectUserByUserName(user.getUserName()); SysUser u = userMapper.selectUserByUserName(user.getUserName());
if (StringUtils.isNull(u)) if (StringUtils.isNull(u))

View File

@ -314,7 +314,37 @@ public class PsyUserProfileServiceImpl implements IPsyUserProfileService
@Override @Override
public int deleteProfileByIds(Long[] profileIds) public int deleteProfileByIds(Long[] profileIds)
{ {
return profileMapper.deleteProfileByIds(profileIds); // 先获取所有要删除的档案记录对应的用户ID
java.util.List<Long> userIdsToDelete = new java.util.ArrayList<>();
for (Long profileId : profileIds)
{
PsyUserProfile profile = profileMapper.selectProfileById(profileId);
if (profile != null && profile.getUserId() != null)
{
userIdsToDelete.add(profile.getUserId());
}
}
// 删除档案
int result = profileMapper.deleteProfileByIds(profileIds);
// 删除对应的用户
if (!userIdsToDelete.isEmpty())
{
Long[] userIds = userIdsToDelete.toArray(new Long[0]);
try
{
userService.deleteUserByIds(userIds);
log.info("删除档案同时删除了对应的用户用户ID: {}", userIdsToDelete);
}
catch (Exception e)
{
log.error("删除用户失败用户ID: {}", userIdsToDelete, e);
// 即使删除用户失败档案已经删除也返回成功
}
}
return result;
} }
private void validateInfoNumberUnique(String infoNumber, Long profileId) private void validateInfoNumberUnique(String infoNumber, Long profileId)
@ -336,16 +366,22 @@ public class PsyUserProfileServiceImpl implements IPsyUserProfileService
{ {
throw new ServiceException("信息编号不能为空"); throw new ServiceException("信息编号不能为空");
} }
SysUser user = new SysUser(); SysUser user = new SysUser();
// 使用信息编号作为登录账号用户名
String loginAccount = profile.getInfoNumber(); String loginAccount = profile.getInfoNumber();
user.setUserName(loginAccount);
// 不设置userId让数据库自动生成
// userId和信息编号不需要保持一致信息编号用于登录
user.setUserName(loginAccount); // 登录账号 = 信息编号
user.setNickName(StringUtils.isNotEmpty(profile.getUserName()) ? profile.getUserName() : loginAccount); user.setNickName(StringUtils.isNotEmpty(profile.getUserName()) ? profile.getUserName() : loginAccount);
user.setPhonenumber(profile.getPhone()); user.setPhonenumber(profile.getPhone());
user.setDeptId(profile.getDeptId()); user.setDeptId(profile.getDeptId());
user.setStatus("0"); user.setStatus("0");
user.setPassword(SecurityUtils.encryptPassword(resolveInitPassword())); user.setPassword(SecurityUtils.encryptPassword(resolveInitPassword()));
user.setCreateBy(SecurityUtils.getUsername()); user.setCreateBy(SecurityUtils.getUsername());
user.setRemark("由用户档案自动创建"); user.setRemark("由用户档案自动创建(登录账号:" + loginAccount + "");
Long studentRoleId = resolveStudentRoleId(); Long studentRoleId = resolveStudentRoleId();
if (studentRoleId != null) if (studentRoleId != null)
{ {
@ -355,11 +391,16 @@ public class PsyUserProfileServiceImpl implements IPsyUserProfileService
{ {
log.warn("未找到学生角色,自动创建的用户将没有角色,请检查角色配置"); log.warn("未找到学生角色,自动创建的用户将没有角色,请检查角色配置");
} }
if (!userService.checkUserNameUnique(user)) if (!userService.checkUserNameUnique(user))
{ {
throw new ServiceException("登录账号 '" + loginAccount + "' 已存在,请更换信息编号"); throw new ServiceException("登录账号 '" + loginAccount + "' 已存在,请更换信息编号");
} }
log.info("自动创建用户,登录账号: {}", loginAccount);
userService.insertUser(user); userService.insertUser(user);
log.info("用户创建成功用户ID: {}, 登录账号: {}", user.getUserId(), user.getUserName());
return user; return user;
} }
@ -418,5 +459,86 @@ public class PsyUserProfileServiceImpl implements IPsyUserProfileService
} }
return null; return null;
} }
/**
* 导入用户档案数据
*
* @param profileList 用户档案数据列表
* @param isUpdateSupport 是否更新支持如果已存在则进行更新数据
* @param operName 操作用户
* @return 结果
*/
@Override
public String importProfile(List<PsyUserProfile> profileList, Boolean isUpdateSupport, String operName)
{
if (StringUtils.isNull(profileList) || profileList.size() == 0)
{
throw new ServiceException("导入用户档案数据不能为空!");
}
int successNum = 0;
int failureNum = 0;
StringBuilder successMsg = new StringBuilder();
StringBuilder failureMsg = new StringBuilder();
for (PsyUserProfile profile : profileList)
{
try
{
// 设置创建者
profile.setCreateBy(operName);
// 验证信息编号
if (StringUtils.isEmpty(profile.getInfoNumber()))
{
failureNum++;
failureMsg.append("<br/>").append(failureNum).append("、档案信息编号为空");
continue;
}
// 根据信息编号查询是否存在
PsyUserProfile existProfile = profileMapper.selectProfileByInfoNumber(profile.getInfoNumber());
if (StringUtils.isNull(existProfile))
{
// 新增档案
this.insertProfile(profile);
successNum++;
successMsg.append("<br/>").append(successNum).append("、信息编号 ").append(profile.getInfoNumber()).append(" 导入成功");
}
else if (isUpdateSupport)
{
// 更新档案
profile.setProfileId(existProfile.getProfileId());
profile.setUserId(existProfile.getUserId());
profile.setUpdateBy(operName);
this.updateProfile(profile);
successNum++;
successMsg.append("<br/>").append(successNum).append("、信息编号 ").append(profile.getInfoNumber()).append(" 更新成功");
}
else
{
failureNum++;
failureMsg.append("<br/>").append(failureNum).append("、信息编号 ").append(profile.getInfoNumber()).append(" 已存在");
}
}
catch (Exception e)
{
failureNum++;
String msg = "<br/>" + failureNum + "、信息编号 " + profile.getInfoNumber() + " 导入失败:";
failureMsg.append(msg).append(e.getMessage());
log.error(msg, e);
}
}
if (failureNum > 0)
{
failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:");
throw new ServiceException(failureMsg.toString());
}
else
{
successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:");
}
return successMsg.toString();
}
} }

View File

@ -65,5 +65,15 @@ public interface IPsyUserProfileService
* @return 结果 * @return 结果
*/ */
public int deleteProfileByIds(Long[] profileIds); public int deleteProfileByIds(Long[] profileIds);
/**
* 导入用户档案数据
*
* @param profileList 用户档案数据列表
* @param isUpdateSupport 是否更新支持如果已存在则进行更新数据
* @param operName 操作用户
* @return 结果
*/
public String importProfile(List<PsyUserProfile> profileList, Boolean isUpdateSupport, String operName);
} }

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper <!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
@ -152,7 +152,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<insert id="insertUser" parameterType="SysUser" useGeneratedKeys="true" keyProperty="userId"> <insert id="insertUser" parameterType="SysUser" useGeneratedKeys="true" keyProperty="userId">
insert into sys_user( insert into sys_user(
<if test="userId != null and userId != 0">user_id,</if>
<if test="deptId != null and deptId != 0">dept_id,</if> <if test="deptId != null and deptId != 0">dept_id,</if>
<if test="userName != null and userName != ''">user_name,</if> <if test="userName != null and userName != ''">user_name,</if>
<if test="nickName != null and nickName != ''">nick_name,</if> <if test="nickName != null and nickName != ''">nick_name,</if>
@ -167,7 +166,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="remark != null and remark != ''">remark,</if> <if test="remark != null and remark != ''">remark,</if>
create_time create_time
)values( )values(
<if test="userId != null and userId != ''">#{userId},</if>
<if test="deptId != null and deptId != ''">#{deptId},</if> <if test="deptId != null and deptId != ''">#{deptId},</if>
<if test="userName != null and userName != ''">#{userName},</if> <if test="userName != null and userName != ''">#{userName},</if>
<if test="nickName != null and nickName != ''">#{nickName},</if> <if test="nickName != null and nickName != ''">#{nickName},</if>

View File

@ -106,6 +106,7 @@ export const constantRoutes = [
{ {
path: '/student/tests', path: '/student/tests',
component: () => import('@/views/student/tests'), component: () => import('@/views/student/tests'),
name: 'StudentTests',
hidden: true, hidden: true,
meta: { title: '心理测试题' } meta: { title: '心理测试题' }
}, },

View File

@ -9,14 +9,7 @@
@keyup.enter.native="handleQuery" @keyup.enter.native="handleQuery"
/> />
</el-form-item> </el-form-item>
<el-form-item label="用户ID" prop="userId"> <!-- 用户ID已隐藏只使用信息编号 -->
<el-input
v-model="queryParams.userId"
placeholder="请输入用户ID"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="手机号码" prop="phone"> <el-form-item label="手机号码" prop="phone">
<el-input <el-input
v-model="queryParams.phone" v-model="queryParams.phone"
@ -85,13 +78,33 @@
v-hasPermi="['psychology:profile:remove']" v-hasPermi="['psychology:profile:remove']"
>删除</el-button> >删除</el-button>
</el-col> </el-col>
<el-col :span="1.5">
<el-button
type="info"
plain
icon="el-icon-upload2"
size="mini"
@click="handleImport"
v-hasPermi="['psychology:profile:import']"
>导入</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="el-icon-download"
size="mini"
@click="handleExport"
v-hasPermi="['psychology:profile:export']"
>导出</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row> </el-row>
<el-table ref="profileTable" v-loading="loading" :data="profileList" @selection-change="handleSelectionChange"> <el-table ref="profileTable" v-loading="loading" :data="profileList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" /> <el-table-column type="selection" width="55" align="center" />
<el-table-column label="序号" align="center" prop="profileId" width="80" /> <el-table-column label="序号" align="center" prop="profileId" width="80" />
<el-table-column label="用户ID" align="center" prop="userId" width="100" /> <!-- 用户ID列已隐藏只显示信息编号 -->
<el-table-column label="信息编号" align="center" prop="infoNumber" width="120" /> <el-table-column label="信息编号" align="center" prop="infoNumber" width="120" />
<el-table-column label="档案类型" align="center" prop="profileType" width="120"> <el-table-column label="档案类型" align="center" prop="profileType" width="120">
<template slot-scope="scope"> <template slot-scope="scope">
@ -335,12 +348,32 @@
<el-button @click="cancelUser"> </el-button> <el-button @click="cancelUser"> </el-button>
</div> </div>
</el-dialog> </el-dialog>
<!-- 用户档案导入对话框 -->
<el-dialog :title="upload.title" :visible.sync="upload.open" width="400px" append-to-body>
<el-upload ref="upload" :limit="1" accept=".xlsx, .xls" :headers="upload.headers" :action="upload.url + '?updateSupport=' + upload.updateSupport" :disabled="upload.isUploading" :on-progress="handleFileUploadProgress" :on-success="handleFileSuccess" :auto-upload="false" drag>
<i class="el-icon-upload"></i>
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
<div class="el-upload__tip text-center" slot="tip">
<div class="el-upload__tip" slot="tip">
<el-checkbox v-model="upload.updateSupport" />是否更新已经存在的用户档案数据
</div>
<span>仅允许导入xlsxlsx格式文件</span>
<el-link type="primary" :underline="false" style="font-size: 12px; vertical-align: baseline" @click="importTemplate">下载模板</el-link>
</div>
</el-upload>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitFileForm"> </el-button>
<el-button @click="upload.open = false"> </el-button>
</div>
</el-dialog>
</div> </div>
</template> </template>
<script> <script>
import { listProfile, getProfile, getProfileByUserId, delProfile, addProfile, updateProfile, getUserInfo, addUserInProfile, getUserInfoById, updateUserInProfile, delUserInProfile } from "@/api/psychology/profile" import { listProfile, getProfile, getProfileByUserId, delProfile, addProfile, updateProfile, getUserInfo, addUserInProfile, getUserInfoById, updateUserInProfile, delUserInProfile } from "@/api/psychology/profile"
import { deptTreeSelect } from "@/api/system/user" import { deptTreeSelect } from "@/api/system/user"
import { getToken } from "@/utils/auth"
import Treeselect from "@riophae/vue-treeselect" import Treeselect from "@riophae/vue-treeselect"
import "@riophae/vue-treeselect/dist/vue-treeselect.css" import "@riophae/vue-treeselect/dist/vue-treeselect.css"
@ -445,6 +478,21 @@ export default {
trigger: "blur" trigger: "blur"
} }
] ]
},
//
upload: {
//
open: false,
//
title: "",
//
isUploading: false,
//
updateSupport: 0,
//
headers: { Authorization: "Bearer " + getToken() },
//
url: process.env.VUE_APP_BASE_API + "/psychology/profile/importData"
} }
} }
}, },
@ -503,27 +551,11 @@ export default {
} }
this.resetForm("form") this.resetForm("form")
}, },
// userId //
handleInfoNumberInput(value) { handleInfoNumberInput(value) {
// //
const numericValue = value.replace(/\D/g, '') const numericValue = value.replace(/\D/g, '')
this.form.infoNumber = numericValue this.form.infoNumber = numericValue
// userId
if (numericValue) {
this.form.userId = parseInt(numericValue)
} else {
this.form.userId = undefined
}
},
// userIdinfoNumber
syncUserIdWithInfoNumber() {
if (this.form.infoNumber) {
// userId
this.form.userId = parseInt(this.form.infoNumber)
} else if (this.form.userId) {
// userIduserId
this.form.infoNumber = this.form.userId.toString()
}
}, },
// //
handleUserNameInput(value) { handleUserNameInput(value) {
@ -690,8 +722,6 @@ export default {
getProfile(row.profileId).then(response => { getProfile(row.profileId).then(response => {
if (response.data) { if (response.data) {
this.form = response.data this.form = response.data
// userId infoNumber
this.syncUserIdWithInfoNumber()
this.open = true this.open = true
this.title = "修改用户档案" this.title = "修改用户档案"
} else { } else {
@ -714,8 +744,6 @@ export default {
if (response.data && response.data.profileId) { if (response.data && response.data.profileId) {
// //
this.form = response.data this.form = response.data
// userId infoNumber
this.syncUserIdWithInfoNumber()
this.open = true this.open = true
this.title = "修改用户档案" this.title = "修改用户档案"
} else { } else {
@ -834,6 +862,42 @@ export default {
this.getList() this.getList()
this.$modal.msgSuccess("删除成功") this.$modal.msgSuccess("删除成功")
}).catch(() => {}) }).catch(() => {})
},
/** 导出按钮操作 */
handleExport() {
this.download('psychology/profile/export', {
...this.queryParams
}, `profile_${new Date().getTime()}.xlsx`)
},
/** 导入按钮操作 */
handleImport() {
this.upload.title = "用户档案导入"
this.upload.open = true
},
/** 下载模板操作 */
importTemplate() {
window.location.href = process.env.VUE_APP_BASE_API + '/psychology/profile/importTemplate'
},
//
handleFileUploadProgress(event, file, fileList) {
this.upload.isUploading = true
},
//
handleFileSuccess(response, file, fileList) {
this.upload.open = false
this.upload.isUploading = false
this.$refs.upload.clearFiles()
this.$alert("<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + response.msg + "</div>", "导入结果", { dangerouslyUseHTMLString: true })
this.getList()
},
//
submitFileForm() {
const file = this.$refs.upload.uploadFiles
if (!file || file.length === 0 || !file[0].name.toLowerCase().endsWith('.xls') && !file[0].name.toLowerCase().endsWith('.xlsx')) {
this.$modal.msgError('请选择后缀为 "xls"或"xlsx"的文件。')
return
}
this.$refs.upload.submit()
} }
} }
} }

View File

@ -116,23 +116,10 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="报告摘要" prop="summary"> <el-form-item label="报告摘要" prop="summary">
<el-input <Editor v-model="editForm.summary" :min-height="150" />
v-model="editForm.summary"
type="textarea"
:rows="4"
placeholder="请输入报告摘要"
/>
</el-form-item> </el-form-item>
<el-form-item label="报告内容" prop="reportContent"> <el-form-item label="报告内容" prop="reportContent">
<el-input <Editor v-model="editForm.reportContent" :min-height="400" />
v-model="editForm.reportContent"
type="textarea"
:rows="15"
placeholder="请输入报告内容支持HTML格式"
/>
<div style="margin-top: 10px; color: #909399; font-size: 12px;">
<i class="el-icon-info"></i> 提示报告内容支持HTML格式可以包含标题段落列表等格式
</div>
</el-form-item> </el-form-item>
</el-form> </el-form>
<div slot="footer" class="dialog-footer"> <div slot="footer" class="dialog-footer">
@ -148,9 +135,11 @@ import { getReport, getReportByAssessmentId, updateReportWithType } from "@/api/
import { getQuestionnaireRankList } from "@/api/psychology/questionnaireAnswer"; import { getQuestionnaireRankList } from "@/api/psychology/questionnaireAnswer";
import request from '@/utils/request'; import request from '@/utils/request';
import axios from 'axios'; import axios from 'axios';
import Editor from "@/components/Editor";
export default { export default {
name: "ReportDetail", name: "ReportDetail",
components: { Editor },
data() { data() {
return { return {
loading: true, loading: true,

View File

@ -147,23 +147,10 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="报告摘要" prop="summary"> <el-form-item label="报告摘要" prop="summary">
<el-input <Editor v-model="editForm.summary" :min-height="150" />
v-model="editForm.summary"
type="textarea"
:rows="4"
placeholder="请输入报告摘要"
/>
</el-form-item> </el-form-item>
<el-form-item label="报告内容" prop="reportContent"> <el-form-item label="报告内容" prop="reportContent">
<el-input <Editor v-model="editForm.reportContent" :min-height="400" />
v-model="editForm.reportContent"
type="textarea"
:rows="15"
placeholder="请输入报告内容支持HTML格式"
/>
<div style="margin-top: 10px; color: #909399; font-size: 12px;">
<i class="el-icon-info"></i> 提示报告内容支持HTML格式可以包含标题段落列表等格式
</div>
</el-form-item> </el-form-item>
</el-form> </el-form>
<div slot="footer" class="dialog-footer"> <div slot="footer" class="dialog-footer">
@ -197,9 +184,11 @@
import { listReport, getReport, delReport, exportReport, updateReportWithType } from "@/api/psychology/report"; import { listReport, getReport, delReport, exportReport, updateReportWithType } from "@/api/psychology/report";
import { loadSasReportData } from "@/services/report/ReportDataMapper"; import { loadSasReportData } from "@/services/report/ReportDataMapper";
import SASReportGenerator from "@/services/report/SASReportGenerator"; import SASReportGenerator from "@/services/report/SASReportGenerator";
import Editor from "@/components/Editor";
export default { export default {
name: "Report", name: "Report",
components: { Editor },
data() { data() {
return { return {
// //

View File

@ -91,10 +91,23 @@ export default {
}) })
} }
}, },
watch: {
//
'$route'(to, from) {
//
if (to.path === '/student/tests') {
this.loadTestList()
}
}
},
created() { created() {
// //
this.loadTestList() this.loadTestList()
}, },
//
activated() {
this.loadTestList()
},
methods: { methods: {
// //
loadTestList() { loadTestList() {
@ -105,9 +118,19 @@ export default {
includeQuestionnaire: true // includeQuestionnaire: true //
}).then(response => { }).then(response => {
// //
this.testList = (response.rows || []).filter(scale => { let filteredList = (response.rows || []).filter(scale => {
return scale.itemCount && scale.itemCount > 0 return scale.itemCount && scale.itemCount > 0
}) })
//
this.testList = filteredList.sort((a, b) => {
// 使 updateTime使 createTime
const timeA = a.updateTime || a.createTime || ''
const timeB = b.updateTime || b.createTime || ''
//
return timeB.localeCompare(timeA)
})
this.loading = false this.loading = false
}).catch(error => { }).catch(error => {
console.error("loadTestList, 加载测试题列表失败:", error) console.error("loadTestList, 加载测试题列表失败:", error)

View File

@ -96,32 +96,10 @@
</el-row> </el-row>
<!-- 添加或修改用户配置对话框 --> <!-- 添加或修改用户配置对话框 -->
<el-dialog :title="title" :visible.sync="open" width="600px" append-to-body> <el-dialog :title="title" :visible.sync="open" width="900px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px"> <el-form ref="form" :model="form" :rules="rules" label-width="120px">
<el-row> <!-- 基本信息 -->
<el-col :span="12"> <el-divider content-position="left">基本信息</el-divider>
<el-form-item label="用户昵称" prop="nickName">
<el-input v-model="form.nickName" placeholder="请输入用户昵称" maxlength="30" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="归属部门" prop="deptId">
<treeselect v-model="form.deptId" :options="enabledDeptOptions" :show-count="true" placeholder="请选择归属部门" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12" v-if="false">
<el-form-item label="手机号码" prop="phonenumber">
<el-input v-model="form.phonenumber" placeholder="请输入手机号码" maxlength="11" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="邮箱" prop="email">
<el-input v-model="form.email" placeholder="请输入邮箱" maxlength="50" />
</el-form-item>
</el-col>
</el-row>
<el-row> <el-row>
<el-col :span="12"> <el-col :span="12">
<el-form-item v-if="form.userId == undefined" label="用户名称" prop="userName"> <el-form-item v-if="form.userId == undefined" label="用户名称" prop="userName">
@ -136,7 +114,31 @@
</el-row> </el-row>
<el-row> <el-row>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="用户性别"> <el-form-item label="用户昵称" prop="nickName">
<el-input v-model="form.nickName" placeholder="请输入用户昵称/姓名" maxlength="30" @input="handleNickNameInput" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="信息编号" prop="infoNumber">
<el-input v-model="form.infoNumber" placeholder="请输入信息编号(仅数字)" @input="handleInfoNumberInput" maxlength="20" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="手机号码" prop="phonenumber">
<el-input v-model="form.phonenumber" placeholder="请输入手机号码" maxlength="11" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="邮箱" prop="email">
<el-input v-model="form.email" placeholder="请输入邮箱" maxlength="50" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="性别" prop="sex">
<el-select v-model="form.sex" placeholder="请选择性别"> <el-select v-model="form.sex" placeholder="请选择性别">
<el-option v-for="dict in dict.type.sys_user_sex" :key="dict.value" :label="dict.label" :value="dict.value"></el-option> <el-option v-for="dict in dict.type.sys_user_sex" :key="dict.value" :label="dict.label" :value="dict.value"></el-option>
</el-select> </el-select>
@ -150,6 +152,78 @@
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
<!-- 心理档案信息 -->
<el-divider content-position="left">心理档案信息</el-divider>
<el-row>
<el-col :span="12">
<el-form-item label="档案类型" prop="profileType">
<el-select v-model="form.profileType" placeholder="请选择档案类型">
<el-option label="标准" value="standard" />
<el-option label="儿童" value="child" />
<el-option label="成人" value="adult" />
<el-option label="老年" value="senior" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="身份证号" prop="idCard">
<el-input v-model="form.idCard" placeholder="请输入身份证号" maxlength="18" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="生日" prop="birthday">
<el-date-picker
v-model="form.birthday"
type="date"
placeholder="选择生日"
value-format="yyyy-MM-dd"
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="学历" prop="education">
<el-input v-model="form.education" placeholder="请输入学历" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="职业" prop="occupation">
<el-input v-model="form.occupation" placeholder="请输入职业" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="归属部门" prop="deptId">
<treeselect v-model="form.deptId" :options="enabledDeptOptions" :show-count="true" placeholder="请选择归属部门" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label="地址" prop="address">
<el-input v-model="form.address" type="textarea" :rows="2" placeholder="请输入地址" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="紧急联系人" prop="emergencyContact">
<el-input v-model="form.emergencyContact" placeholder="请输入紧急联系人" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="紧急联系电话" prop="emergencyPhone">
<el-input v-model="form.emergencyPhone" placeholder="请输入紧急联系电话" maxlength="11" />
</el-form-item>
</el-col>
</el-row>
<!-- 系统权限 -->
<el-divider content-position="left">系统权限</el-divider>
<el-row> <el-row>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="岗位"> <el-form-item label="岗位">
@ -401,10 +475,29 @@ export default {
status: "0", status: "0",
remark: undefined, remark: undefined,
postIds: [], postIds: [],
roleIds: [] roleIds: [],
//
infoNumber: undefined,
profileType: 'standard',
idCard: undefined,
birthday: undefined,
education: undefined,
occupation: undefined,
address: undefined,
emergencyContact: undefined,
emergencyPhone: undefined
} }
this.resetForm("form") this.resetForm("form")
}, },
//
handleInfoNumberInput(value) {
const numericValue = value.replace(/\D/g, '')
this.form.infoNumber = numericValue
},
//
handleNickNameInput(value) {
this.form.nickName = value.replace(/[^\u4e00-\u9fa5]/g, '')
},
/** 搜索按钮操作 */ /** 搜索按钮操作 */
handleQuery() { handleQuery() {
this.queryParams.pageNum = 1 this.queryParams.pageNum = 1