diff --git a/chenhai-admin/src/main/java/com/chenhai/ChenHaiApplication.java b/chenhai-admin/src/main/java/com/chenhai/ChenHaiApplication.java index ad8ac6d..0c953cb 100644 --- a/chenhai-admin/src/main/java/com/chenhai/ChenHaiApplication.java +++ b/chenhai-admin/src/main/java/com/chenhai/ChenHaiApplication.java @@ -6,7 +6,7 @@ import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; /** * 启动程序 - * + * * @author chenhai */ @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class }) diff --git a/chenhai-admin/src/main/java/com/chenhai/web/controller/muhu/MuhuUserController.java b/chenhai-admin/src/main/java/com/chenhai/web/controller/muhu/MuhuUserController.java index fc3ade2..815575c 100644 --- a/chenhai-admin/src/main/java/com/chenhai/web/controller/muhu/MuhuUserController.java +++ b/chenhai-admin/src/main/java/com/chenhai/web/controller/muhu/MuhuUserController.java @@ -273,24 +273,29 @@ public class MuhuUserController extends BaseController // 查询实名认证状态 Long userId = user.getUserId(); if (userId != null) { - // 方法1:直接查询牧户用户表 + // 直接查询牧户用户表 SysMuhuUser muhuUser = sysMuhuUserService.selectSysMuhuUserByUserId(userId); if (muhuUser != null && muhuUser.getAuthStatus() != null) { // 获取认证状态 String authStatus = muhuUser.getAuthStatus(); - String statusLabel = DictUtils.getDictLabel("auth_status", authStatus, "未认证"); - - // 根据状态判断是否已认证 - boolean isAuthenticated = "1".equals(authStatus); // 假设1表示已认证 result.put("authStatus", authStatus); - // 如果需要,可以添加认证的详细信息 - if (isAuthenticated) { + // 如果已认证,返回完整的认证信息 + if ("已认证".equals(authStatus)) { Map authInfo = new HashMap<>(); - authInfo.put("realName", muhuUser.getRealName()); - authInfo.put("authTime", muhuUser.getAuthTime()); + authInfo.put("realName", muhuUser.getRealName()); // 真实姓名 + authInfo.put("idCard", muhuUser.getIdCard()); // ✅ 身份证号 + authInfo.put("authTime", muhuUser.getAuthTime()); // 认证时间 + + // 身份证脱敏(用于前端显示) + if (muhuUser.getIdCard() != null && muhuUser.getIdCard().length() >= 15) { + String idCard = muhuUser.getIdCard(); + String maskedIdCard = idCard.substring(0, 6) + "********" + idCard.substring(idCard.length() - 4); + authInfo.put("maskedIdCard", maskedIdCard); + } + result.put("authInfo", authInfo); } } else { @@ -298,25 +303,17 @@ public class MuhuUserController extends BaseController result.put("authStatus", "未认证"); } - // ========== 新增:查询兽医个人信息 ========== // 判断用户类型,如果是兽医(01),则查询兽医信息表 if ("01".equals(user.getUserType())) { VetPersonalInfo vetInfo = vetPersonalInfoService.selectVetPersonalInfoByUserId(userId); if (vetInfo != null) { - // 只提取需要的字段 Map vetData = new HashMap<>(); vetData.put("specialty", vetInfo.getSpecialty()); vetData.put("workExperience", vetInfo.getWorkExperience()); vetData.put("expertType", vetInfo.getExpertType()); - - // 可选:如果需要其他兽医字段也可以添加 - // vetData.put("title", vetInfo.getTitle()); - // vetData.put("hospital", vetInfo.getHospital()); - result.put("vetInfo", vetData); } } - // ========== 新增结束 ========== } else { result.put("authStatus", "未认证"); diff --git a/chenhai-admin/src/main/java/com/chenhai/web/controller/vet/VetKnowledgeController.java b/chenhai-admin/src/main/java/com/chenhai/web/controller/vet/VetKnowledgeController.java index 8c373e9..ba55f65 100644 --- a/chenhai-admin/src/main/java/com/chenhai/web/controller/vet/VetKnowledgeController.java +++ b/chenhai-admin/src/main/java/com/chenhai/web/controller/vet/VetKnowledgeController.java @@ -203,4 +203,4 @@ public class VetKnowledgeController extends BaseController } return error("文章不存在或未发布"); } -} \ No newline at end of file +} diff --git a/chenhai-admin/src/main/java/com/chenhai/web/controller/vet/VetUnifiedInfoController.java b/chenhai-admin/src/main/java/com/chenhai/web/controller/vet/VetUnifiedInfoController.java new file mode 100644 index 0000000..4a7c33d --- /dev/null +++ b/chenhai-admin/src/main/java/com/chenhai/web/controller/vet/VetUnifiedInfoController.java @@ -0,0 +1,510 @@ +package com.chenhai.web.controller.vet; + +import com.chenhai.common.annotation.Log; +import com.chenhai.common.core.controller.BaseController; +import com.chenhai.common.core.domain.AjaxResult; +import com.chenhai.common.core.domain.entity.SysRole; +import com.chenhai.common.core.domain.model.LoginUser; +import com.chenhai.common.core.page.TableDataInfo; +import com.chenhai.common.enums.BusinessType; +import com.chenhai.common.utils.SecurityUtils; +import com.chenhai.common.utils.StringUtils; +import com.chenhai.vet.domain.VetPersonalInfo; +import com.chenhai.vet.domain.dto.VetUnifiedInfoDTO; +import com.chenhai.vet.service.IVetPersonalInfoService; +import com.chenhai.vet.service.IVetUnifiedInfoService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * 兽医统一信息Controller + * 整合PC端和小程序的所有接口 + * 遵循RESTful API设计规范 + * + * RESTful接口列表: + * 1. GET /vet/unified/info - 获取当前用户完整信息 + * 2. GET /vet/unified/{userId} - 获取指定用户信息 + * 3. POST /vet/unified - 新增兽医信息 + * 4. PUT /vet/unified - 全量更新兽医信息 + * 5. PATCH /vet/unified/{userId} - 部分更新兽医信息 + * 6. POST /vet/unified/submitAudit - 提交审核 + * 7. GET /vet/unified/statistics - 获取统计信息 + * 8. GET /vet/unified/checkNeed - 检查是否需要填写 + * 9. GET /vet/unified/list - 分页查询列表(管理员) + * 10. POST /vet/unified/audit - 审核兽医信息(管理员) + * + * 已废弃接口(保留向后兼容): + * - GET /vet/unified/info/{userId} - 请使用 GET /vet/unified/{userId} + * - POST /vet/unified/save - 请根据情况使用 POST /vet/unified 或 PUT /vet/unified/{userId} + */ +@RestController +@RequestMapping("/vet/unified") +public class VetUnifiedInfoController extends BaseController { + + private static final Logger log = LoggerFactory.getLogger(VetUnifiedInfoController.class); + + @Autowired + private IVetUnifiedInfoService vetUnifiedInfoService; + + @Autowired + private IVetPersonalInfoService vetPersonalInfoService; + + // ==================== 新RESTful接口 ==================== + + /** + * 获取当前登录用户的完整信息 + * GET /vet/unified/info + * + * 替代:/muhu/user/getUserInfo 和 /vet/info/current + */ + @PreAuthorize("@ss.hasRole('muhu') or @ss.hasRole('vet')") + @GetMapping("/info") + public AjaxResult getCurrentUserInfo() { + try { + Long userId = SecurityUtils.getUserId(); + if (userId == null) { + return error("用户未登录"); + } + + VetUnifiedInfoDTO unifiedInfo = vetUnifiedInfoService.getCurrentUserInfo(); + return success(unifiedInfo); + + } catch (Exception e) { + log.error("获取用户信息失败", e); + return error("获取用户信息失败: " + e.getMessage()); + } + } + + /** + * 根据用户ID获取信息(RESTful风格) + * GET /vet/unified/{userId} + * + * 替代:/sys/vet/audit/full/{id} 和 /vet/info/full/{id} + */ + @GetMapping("/{userId}") + @PreAuthorize("@ss.hasPermi('sys:vetAudit:view') or @ss.hasRole('muhu') or @ss.hasRole('vet') or @ss.hasRole('vetnotshenhe')") + public AjaxResult getUserInfo(@PathVariable Long userId) { + try { + // 权限验证:非管理员只能查看自己的信息 + Long currentUserId = SecurityUtils.getUserId(); + if (!SecurityUtils.isAdmin(currentUserId) && !currentUserId.equals(userId)) { + return error("无权查看他人信息"); + } + + VetUnifiedInfoDTO unifiedInfo = vetUnifiedInfoService.getUserInfoByUserId(userId); + if (unifiedInfo == null) { + return error("用户信息不存在"); + } + return success(unifiedInfo); + } catch (Exception e) { + log.error("获取用户信息失败", e); + return error("获取用户信息失败: " + e.getMessage()); + } + } + + /** + * 新增兽医信息 + * POST /vet/unified + * + * 替代:/vet/qualification/submit (新增部分) 和 /vet/info/add + */ + @PreAuthorize("@ss.hasRole('muhu') or @ss.hasRole('vet') or @ss.hasRole('vetnotshenhe')") + @PostMapping + @Log(title = "兽医信息", businessType = BusinessType.INSERT) + public AjaxResult createVetInfo(@RequestBody VetUnifiedInfoDTO vetInfo) { + try { + Long userId = SecurityUtils.getUserId(); + if (userId == null) { + return error("用户未登录"); + } + + // 检查是否已存在 + VetPersonalInfo existing = vetPersonalInfoService.selectVetPersonalInfoByUserId(userId); + if (existing != null) { + return error("兽医信息已存在,请使用更新接口"); + } + + // 设置用户ID + vetInfo.setUserId(userId); + + // 转换为Map调用service + Map requestData = convertDtoToMap(vetInfo); + + VetUnifiedInfoDTO result = vetUnifiedInfoService.saveVetInfo(requestData); + return AjaxResult.success("创建成功", result); + + } catch (Exception e) { + log.error("创建兽医信息失败", e); + return error("创建失败: " + e.getMessage()); + } + } + + /** + * 全量更新兽医信息 + * PUT /vet/unified/{userId} + * + * 替代:/vet/qualification/submit (修改部分) 和 /vet/info/edit + */ + @PutMapping + @PreAuthorize("@ss.hasRole('muhu') or @ss.hasRole('vet') or @ss.hasRole('vetnotshenhe')") + @Log(title = "兽医信息", businessType = BusinessType.UPDATE) + public AjaxResult updateCurrentUserVetInfo(@RequestBody VetUnifiedInfoDTO vetInfo) { + try { + Long currentUserId = SecurityUtils.getUserId(); + if (currentUserId == null) { + return error("用户未登录"); + } + + log.info("更新兽医信息 - userId: {}, data: {}", currentUserId, vetInfo); + + // 检查记录是否存在 + VetPersonalInfo existing = vetPersonalInfoService.selectVetPersonalInfoByUserId(currentUserId); + if (existing == null) { + return error("兽医信息不存在,请先创建"); + } + + // 设置用户ID + vetInfo.setUserId(currentUserId); + vetInfo.setPersonalInfoId(existing.getId()); + + // 转换为Map调用service + Map requestData = convertDtoToMap(vetInfo); + requestData.put("personalInfoId", existing.getId()); + requestData.put("id", existing.getId()); + + // 调用保存方法 + vetUnifiedInfoService.saveVetInfo(requestData); + + // 重新查询最新数据 + VetUnifiedInfoDTO result = vetUnifiedInfoService.getCurrentUserInfo(); + + log.info("更新成功 - userId: {}, result: {}", currentUserId, result); + return AjaxResult.success("更新成功", result); + + } catch (Exception e) { + log.error("更新兽医信息失败", e); + return error("更新失败: " + e.getMessage()); + } + } + + /** + * 部分更新兽医信息 + * PATCH /vet/unified/{userId} + * 支持只提交需要修改的字段 + */ + @PatchMapping("/{userId}") + @PreAuthorize("@ss.hasRole('muhu') or @ss.hasRole('vet') or @ss.hasRole('vetnotshenhe')") + @Log(title = "兽医信息", businessType = BusinessType.UPDATE) + public AjaxResult partialUpdateVetInfo(@PathVariable Long userId, @RequestBody Map updates) { + try { + Long currentUserId = SecurityUtils.getUserId(); + if (currentUserId == null) { + return error("用户未登录"); + } + + // 权限验证:只能修改自己的信息 + if (!currentUserId.equals(userId)) { + return error("无权修改他人信息"); + } + + // 检查记录是否存在 + VetPersonalInfo existing = vetPersonalInfoService.selectVetPersonalInfoByUserId(userId); + if (existing == null) { + return error("兽医信息不存在,请先创建"); + } + + // 添加必要的ID信息 + updates.put("userId", userId); + updates.put("personalInfoId", existing.getId()); + + VetUnifiedInfoDTO result = vetUnifiedInfoService.saveVetInfo(updates); + return AjaxResult.success("更新成功", result); + + } catch (Exception e) { + log.error("更新兽医信息失败", e); + return error("更新失败: " + e.getMessage()); + } + } + + /** + * 提交审核 + * POST /vet/unified/submitAudit + * + * 替代:/vet/qualification/submit 中的审核提交部分 + */ + @PreAuthorize("@ss.hasRole('muhu') or @ss.hasRole('vet') or @ss.hasRole('vetnotshenhe')") + @PostMapping("/submitAudit") + @Log(title = "兽医资质", businessType = BusinessType.OTHER) + public AjaxResult submitForAudit(@RequestBody(required = false) Map requestData) { + try { + Long userId = SecurityUtils.getUserId(); + if (userId == null) { + return error("用户未登录"); + } + + log.info("保存并提交审核 - userId: {}", userId); + + // 如果有数据,先保存 + if (requestData != null && !requestData.isEmpty()) { + // 检查是否需要添加personalInfoId + if (!requestData.containsKey("personalInfoId") && !requestData.containsKey("id")) { + VetPersonalInfo existing = vetPersonalInfoService.selectVetPersonalInfoByUserId(userId); + if (existing != null) { + requestData.put("personalInfoId", existing.getId()); + } + } + vetUnifiedInfoService.saveVetInfo(requestData); + } + + // 调用提交审核接口 + boolean submitSuccess = vetUnifiedInfoService.submitForAudit(userId); + + if (submitSuccess) { + return AjaxResult.success("提交审核成功"); + } else { + return AjaxResult.warn("提交审核失败,请稍后重试"); + } + + } catch (Exception e) { + log.error("提交审核失败", e); + return error("操作失败: " + e.getMessage()); + } + } + + /** + * 获取信息统计 + * GET /vet/unified/statistics + * + * 替代:/vet/qualification/statistics/{userId} + */ + @PreAuthorize("@ss.hasRole('muhu') or @ss.hasRole('vet')") + @GetMapping("/statistics") + public AjaxResult getStatistics() { + try { + Long userId = SecurityUtils.getUserId(); + if (userId == null) { + return error("用户未登录"); + } + + Map statistics = vetUnifiedInfoService.getStatistics(userId); + return success(statistics); + + } catch (Exception e) { + log.error("获取统计信息失败", e); + return error("获取失败: " + e.getMessage()); + } + } + + /** + * 检查是否需要填写资质(登录后调用) + * GET /vet/unified/checkNeed + * + * 替代:/vet/qualification/checkNeedQualification + */ + @PreAuthorize("@ss.hasRole('muhu') or @ss.hasRole('vet') or @ss.hasRole('vetnotshenhe')") + @GetMapping("/checkNeed") + public AjaxResult checkNeedQualification() { + try { + Long userId = SecurityUtils.getUserId(); + if (userId == null) { + return error("用户未登录"); + } + + Map result = vetUnifiedInfoService.checkNeedQualification(userId); + return success(result); + + } catch (Exception e) { + log.error("检查资质需求失败", e); + return error("检查失败: " + e.getMessage()); + } + } + + /** + * 分页查询兽医信息列表(管理员用) + * GET /vet/unified/list + * + * 替代:/sys/vet/audit/list 和 /vet/info/list + */ + @GetMapping("/list") + @PreAuthorize("@ss.hasPermi('sys:vetAudit:list') or @ss.hasRole('muhu') or @ss.hasRole('manger') or @ss.hasRole('vet') or @ss.hasRole('vetnotshenhe')") + public TableDataInfo list(VetUnifiedInfoDTO query) { + startPage(); + + // 获取当前登录用户 + LoginUser loginUser = SecurityUtils.getLoginUser(); + + // 判断是否是管理员(muhu 或 manger) + boolean isAdmin = false; + if (loginUser != null && loginUser.getUser() != null) { + List roles = loginUser.getUser().getRoles(); + for (SysRole role : roles) { + String roleKey = role.getRoleKey(); + if ("muhu".equals(roleKey) || "manger".equals(roleKey)) { + isAdmin = true; + break; + } + } + } + + // 如果不是管理员,只能看自己的数据 + if (!isAdmin) { + if (query == null) { + query = new VetUnifiedInfoDTO(); + } + query.setUserId(SecurityUtils.getUserId()); + log.debug("普通用户查询自己的信息 - userId: {}", SecurityUtils.getUserId()); + } else { + log.debug("管理员查询所有信息"); + } + + List list = vetUnifiedInfoService.listVetInfo(query); + return getDataTable(list); + } + + /** + * 审核兽医信息(管理员用) + * POST /vet/unified/audit + * + * 替代:/sys/vet/audit/qualificationAudit + */ + @PostMapping("/audit") + @PreAuthorize("@ss.hasPermi('sys:vetAudit:audit') or @ss.hasRole('muhu')") + @Log(title = "兽医审核", businessType = BusinessType.UPDATE) + public AjaxResult auditVetInfo(@RequestBody Map auditData) { + try { + Long userId = getAuditUserId(auditData); + String auditStatus = (String) auditData.get("auditStatus"); + String auditOpinion = (String) auditData.get("auditOpinion"); + Long auditorId = SecurityUtils.getUserId(); + + if (userId == null || StringUtils.isEmpty(auditStatus)) { + return error("用户ID和审核状态不能为空"); + } + + boolean success = vetUnifiedInfoService.auditVetInfo(userId, auditStatus, auditOpinion, auditorId); + if (success) { + return success("审核成功"); + } else { + return error("审核失败"); + } + + } catch (Exception e) { + log.error("审核失败", e); + return error("审核失败: " + e.getMessage()); + } + } + + // ==================== 已废弃接口(保留向后兼容) ==================== + +// /** +// * 已废弃:根据用户ID获取信息 +// * 请使用 GET /vet/unified/{userId} +// */ +// @Deprecated +// @GetMapping("/info/{userId}") +// @PreAuthorize("@ss.hasPermi('sys:vetAudit:view') or @ss.hasRole('muhu') or @ss.hasRole('vet')") +// public AjaxResult getUserInfoByUserId(@PathVariable Long userId) { +// log.warn("调用已废弃接口 /vet/unified/info/{},请改用 /vet/unified/{}", userId, userId); +// return getUserInfo(userId); // 调用新接口 +// } + +// /** +// * 已废弃:保存/更新兽医信息 +// * 请根据情况使用 POST /vet/unified 或 PUT /vet/unified/{userId} +// */ +// @Deprecated +// @PostMapping("/save") +// @PreAuthorize("@ss.hasRole('muhu') or @ss.hasRole('vet') or @ss.hasRole('vetnotshenhe')") +// @Log(title = "兽医信息", businessType = BusinessType.UPDATE) +// public AjaxResult saveVetInfo(@RequestBody Map requestData) { +// log.warn("调用已废弃接口 /vet/unified/save,请根据情况使用 POST /vet/unified 或 PUT /vet/unified/{userId}"); +// +// try { +// Long userId = SecurityUtils.getUserId(); +// if (userId == null) { +// return error("用户未登录"); +// } +// +// log.info("保存兽医信息 - userId: {}, data: {}", userId, requestData); +// +// // 从请求中获取 id(如果有) +// Long targetId = null; +// if (requestData.containsKey("id")) { +// Object idObj = requestData.get("id"); +// if (idObj instanceof Number) { +// targetId = ((Number) idObj).longValue(); +// } else if (idObj instanceof String) { +// try { +// targetId = Long.parseLong((String) idObj); +// } catch (NumberFormatException e) { +// return error("ID格式不正确"); +// } +// } +// } +// +// // 如果传了ID,验证该记录是否属于当前用户 +// if (targetId != null && targetId > 0) { +// VetPersonalInfo existingInfo = vetPersonalInfoService.selectVetPersonalInfoById(targetId); +// if (existingInfo == null) { +// return error("记录不存在"); +// } +// // 验证所有权:只能修改自己的信息 +// if (!existingInfo.getUserId().equals(userId)) { +// return error("无权修改他人的信息"); +// } +// // 将ID放入requestData,确保service层能获取到 +// requestData.put("personalInfoId", targetId); +// } +// +// VetUnifiedInfoDTO result = vetUnifiedInfoService.saveVetInfo(requestData); +// return AjaxResult.success("保存成功", result); +// +// } catch (Exception e) { +// log.error("保存兽医信息失败", e); +// return error("保存失败: " + e.getMessage()); +// } +// } + + // ==================== 私有辅助方法 ==================== + + /** + * 从请求数据中获取用户ID + */ + private Long getAuditUserId(Map auditData) { + Object userIdObj = auditData.get("userId"); + if (userIdObj == null) { + userIdObj = auditData.get("id"); + } + if (userIdObj == null) { + userIdObj = auditData.get("personalInfoId"); + } + + if (userIdObj instanceof Number) { + return ((Number) userIdObj).longValue(); + } else if (userIdObj instanceof String) { + try { + return Long.parseLong((String) userIdObj); + } catch (NumberFormatException e) { + return null; + } + } + return null; + } + + /** + * 将DTO对象转换为Map + */ + private Map convertDtoToMap(VetUnifiedInfoDTO dto) { + // 这里可以使用ObjectMapper,或者手动转换 + // 简单起见,这里假设service层能直接处理DTO + // 实际项目中可以使用JSON工具转换 + return (Map) com.alibaba.fastjson.JSON.toJSON(dto); + } +} diff --git a/chenhai-system/src/main/java/com/chenhai/system/service/impl/SysMuhuUserServiceImpl.java b/chenhai-system/src/main/java/com/chenhai/system/service/impl/SysMuhuUserServiceImpl.java index c3d1d2c..b1d30d0 100644 --- a/chenhai-system/src/main/java/com/chenhai/system/service/impl/SysMuhuUserServiceImpl.java +++ b/chenhai-system/src/main/java/com/chenhai/system/service/impl/SysMuhuUserServiceImpl.java @@ -2,12 +2,19 @@ package com.chenhai.system.service.impl; import java.util.List; import com.chenhai.common.utils.DateUtils; +import com.chenhai.common.utils.StringUtils; +import com.chenhai.common.core.domain.entity.SysUser; +import com.chenhai.system.service.ISysUserService; +import com.chenhai.vet.domain.VetQualification; +import com.chenhai.vet.mapper.VetQualificationMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.chenhai.system.mapper.SysMuhuUserMapper; import com.chenhai.system.domain.SysMuhuUser; import com.chenhai.system.service.ISysMuhuUserService; import org.springframework.transaction.annotation.Transactional; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * 牧户用户Service业务层处理 @@ -18,9 +25,17 @@ import org.springframework.transaction.annotation.Transactional; @Service public class SysMuhuUserServiceImpl implements ISysMuhuUserService { + private static final Logger log = LoggerFactory.getLogger(SysMuhuUserServiceImpl.class); + @Autowired private SysMuhuUserMapper sysMuhuUserMapper; + @Autowired + private VetQualificationMapper vetQualificationMapper; + + @Autowired + private ISysUserService sysUserService; + /** * 查询牧户用户 */ @@ -114,10 +129,10 @@ public class SysMuhuUserServiceImpl implements ISysMuhuUserService } /** - * 提交实名认证 + * 提交实名认证 - 增强版,支持同步到兽医资质表 */ @Override - @Transactional + @Transactional(rollbackFor = Exception.class) public boolean submitRealNameAuth(Long userId, String realName, String idCard) { // 1. 参数验证 if (userId == null) { @@ -165,33 +180,184 @@ public class SysMuhuUserServiceImpl implements ISysMuhuUserService } // 6. 保存认证信息 + boolean result; if (existingUser == null) { // 新增记录 SysMuhuUser newUser = new SysMuhuUser(); newUser.setUserId(userId); newUser.setRealName(realName); newUser.setIdCard(idCard); - newUser.setAuthStatus("已认证"); // 设置为"已认证" + newUser.setAuthStatus("已认证"); newUser.setAuthTime(DateUtils.getNowDate()); - return sysMuhuUserMapper.insertSysMuhuUser(newUser) > 0; + result = sysMuhuUserMapper.insertSysMuhuUser(newUser) > 0; + if (result) { + log.info("新增实名认证记录成功,userId: {}, realName: {}", userId, realName); + } } else { // 更新记录 existingUser.setRealName(realName); existingUser.setIdCard(idCard); - existingUser.setAuthStatus("已认证"); // 设置为"已认证" + existingUser.setAuthStatus("已认证"); existingUser.setAuthTime(DateUtils.getNowDate()); existingUser.setUpdateTime(DateUtils.getNowDate()); - return sysMuhuUserMapper.updateSysMuhuUser(existingUser) > 0; + result = sysMuhuUserMapper.updateSysMuhuUser(existingUser) > 0; + if (result) { + log.info("更新实名认证记录成功,userId: {}, realName: {}", userId, realName); + } + } + + // ========== 新增:实名认证成功后同步到兽医资质表 ========== + if (result) { + syncToVetQualification(userId, realName, idCard); + } + + return result; + } + + /** + * 同步实名信息到兽医资质表 + * + * @param userId 用户ID + * @param realName 真实姓名 + * @param idCard 身份证号 + */ + private void syncToVetQualification(Long userId, String realName, String idCard) { + try { + log.info("开始同步实名信息到兽医资质表,userId: {}, realName: {}", userId, realName); + + // 查询用户类型 + SysUser user = sysUserService.selectUserById(userId); + if (user == null) { + log.warn("用户不存在,无法同步到兽医资质表,userId: {}", userId); + return; + } + + // 只有兽医类型(01)才需要同步到资质表 + if (!"01".equals(user.getUserType())) { + log.info("用户类型不是兽医,无需同步到资质表,userId: {}, userType: {}", userId, user.getUserType()); + return; + } + + // 查询是否已有资质记录 + VetQualification qualification = vetQualificationMapper.selectVetQualificationByUserId(userId); + + if (qualification == null) { + // 创建新资质记录,只填充基本信息 + qualification = new VetQualification(); + qualification.setUserId(userId); + qualification.setRealName(realName); + qualification.setIdCard(idCard); + qualification.setAuditStatus("0"); // 待审核 +// qualification.setApplyTime(new Date()); + + // 创建空的证书JSON + qualification.setCertificatesJson("[]"); + + // 设置默认值 +// qualification.setCreateTime(new Date()); +// qualification.setUpdateTime(new Date()); + + // 设置创建人(可选) + qualification.setCreateBy(user.getUserName()); + + int insertResult = vetQualificationMapper.insertVetQualification(qualification); + + if (insertResult > 0) { + log.info("实名认证后自动创建兽医资质记录成功,userId: {}, qualificationId: {}", + userId, qualification.getQualificationId()); + } else { + log.warn("实名认证后自动创建兽医资质记录失败,userId: {}", userId); + } + + } else { + // 更新现有资质记录(只更新姓名和身份证,不覆盖已提交的证书) + boolean needUpdate = false; + StringBuilder updateFields = new StringBuilder(); + + if (StringUtils.isEmpty(qualification.getRealName())) { + qualification.setRealName(realName); + needUpdate = true; + updateFields.append("realName "); + } else if (!realName.equals(qualification.getRealName())) { + // 如果已有的姓名与实名认证不一致,记录警告但不自动覆盖 + log.warn("兽医资质表已有姓名[{}]与实名认证姓名[{}]不一致,userId: {}", + qualification.getRealName(), realName, userId); + } + + if (StringUtils.isEmpty(qualification.getIdCard())) { + qualification.setIdCard(idCard); + needUpdate = true; + updateFields.append("idCard "); + } else if (!idCard.equals(qualification.getIdCard())) { + // 如果已有的身份证与实名认证不一致,记录警告但不自动覆盖 + log.warn("兽医资质表已有身份证[{}]与实名认证身份证[{}]不一致,userId: {}", + qualification.getIdCard(), idCard, userId); + } + + if (needUpdate) { +// qualification.setUpdateTime(new Date()); + int updateResult = vetQualificationMapper.updateVetQualification(qualification); + + if (updateResult > 0) { + log.info("实名认证后更新兽医资质基本信息成功,userId: {}, 更新字段: {}", + userId, updateFields.toString()); + } else { + log.warn("实名认证后更新兽医资质基本信息失败,userId: {}", userId); + } + } else { + log.info("兽医资质表基本信息已存在,无需更新,userId: {}", userId); + } + } + + } catch (Exception e) { + // 同步失败不影响实名认证主流程,只记录日志 + log.error("同步实名信息到兽医资质表异常,userId: {}, error: {}", userId, e.getMessage(), e); } } /** - * 验证身份证号格式 + * 验证身份证号格式(增强版) + * + * @param idCard 身份证号 + * @return true-有效,false-无效 */ private boolean isValidIdCard(String idCard) { - // 18位身份证正则 + if (idCard == null || idCard.length() != 18) { + return false; + } + + // 18位身份证正则(包含校验位验证) String regex = "^[1-9]\\d{5}(18|19|20)\\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\\d{3}[0-9X]$"; - return idCard.matches(regex); + if (!idCard.matches(regex)) { + return false; + } + + // 校验码验证(可选,增强安全性) + return validateIdCardCheckCode(idCard); + } + + /** + * 验证身份证校验码 + */ + private boolean validateIdCardCheckCode(String idCard) { + // 加权因子 + int[] factor = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2}; + // 校验码对应值 + char[] parityBit = {'1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'}; + + int sum = 0; + try { + for (int i = 0; i < 17; i++) { + sum += Character.getNumericValue(idCard.charAt(i)) * factor[i]; + } + } catch (Exception e) { + return false; + } + + int mod = sum % 11; + char checkCode = parityBit[mod]; + + return checkCode == idCard.charAt(17); } /** @@ -212,4 +378,4 @@ public class SysMuhuUserServiceImpl implements ISysMuhuUserService // 确保返回有效的认证状态,默认为"未认证" return (authStatus != null && !authStatus.trim().isEmpty()) ? authStatus : "未认证"; } -} \ No newline at end of file +} diff --git a/chenhai-system/src/main/java/com/chenhai/vet/domain/dto/VetUnifiedInfoDTO.java b/chenhai-system/src/main/java/com/chenhai/vet/domain/dto/VetUnifiedInfoDTO.java new file mode 100644 index 0000000..0a8b9ae --- /dev/null +++ b/chenhai-system/src/main/java/com/chenhai/vet/domain/dto/VetUnifiedInfoDTO.java @@ -0,0 +1,673 @@ +package com.chenhai.vet.domain.dto; + +import com.chenhai.common.annotation.Excel; +import com.chenhai.vet.domain.VetQualification; +import com.fasterxml.jackson.annotation.JsonFormat; +import java.util.Date; +import java.util.List; +import java.util.Map; + +/** + * 兽医统一信息DTO + * 整合 VetPersonalInfo 和 VetQualification 的所有字段 + */ +public class VetUnifiedInfoDTO { + + // ========== 用户基本信息 (来自SysUser) ========== + private Long userId; + private String userName; + private String nickName; + private String phonenumber; + private String email; + private String avatar; + private String userType; + private String areaCode; + private String status; + private Date loginDate; + + // ========== 兽医个人信息 (来自VetPersonalInfo) ========== + private Long personalInfoId; // 对应 VetPersonalInfo.id + private String realName; // 真实姓名 + private String gender; // 性别(0男 1女) + @JsonFormat(pattern = "yyyy-MM-dd") + private Date birthday; // 出生日期 + private String idCard; // 身份证号 + private String specialty; // 擅长领域 + private String workExperience; // 工作经验 + private String hospital; // 所属医院 + private String address; // 联系地址 + private String iphone; // 联系方式 + private String introduction; // 个人简介 + private String title; // 职称 + private String phone; // 手机号 + private String expertType; // 专家类型 + private String personalEmail; // 电子邮件地址(兽医表) + private String personalNickName; // 用户昵称(兽医表) + + // ========== 兽医资质信息 (来自VetQualification) ========== + private Long qualificationId; // 资质ID + private String qualificationType; // 资质类型 + private String qualificationTypeLabel; // 资质类型名称 + private String scopeIds; // 经营范围IDs + private String scopeNames; // 经营范围名称 + private List certificates; // 证书列表 + private Integer remindDays; // 提醒天数 + + // 主证书信息(兼容旧版) + private String certName; // 证书名称 + private String certificateNo; // 证书编号 + private String issueOrg; // 发证机构 + private String certificateFiles; // 证书文件 + @JsonFormat(pattern = "yyyy-MM-dd") + private Date issueDate; // 发证日期 + @JsonFormat(pattern = "yyyy-MM-dd") + private Date expireDate; // 到期日期 + private String certStatus; // 证书状态 + private String certStatusLabel; // 证书状态名称 + private Long certId; // 证书ID + private String certificatesJson; // 证书JSON字符串 + + // ========== 审核状态 ========== + private String auditStatus; // 审核状态(0待审核 1通过 2驳回) + private String auditStatusLabel; // 审核状态名称 + private String auditOpinion; // 审核意见 + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date applyTime; // 申请时间 + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date auditTime; // 审核时间 + private Long auditorId; // 审核人ID + private String auditor; // 审核人 + + // ========== 认证状态 (来自SysMuhuUser) ========== + private String authStatus; // 认证状态 + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date authTime; // 认证时间 + private String maskedIdCard; // 脱敏身份证 + + // ========== 统计信息 ========== + private Integer totalCertificates; // 证书总数 + private Integer expiringCount; // 即将过期(30天内) + private Integer expiredCount; // 已过期 + private Integer normalCount; // 正常 + private Integer completeness; // 信息完整度(%) + private Boolean canSubmitAudit; // 是否可以提交审核 + private Map statistics; // 详细统计 + + // ========== 辅助字段 ========== + private Boolean hasPersonalInfo; // 是否有个人信息 + private Boolean hasQualification; // 是否有资质信息 + private Boolean hasCertificates; // 是否有证书 + private String warningLevel; // 警告级别 NORMAL/WARNING/DANGER + private Map extra; // 扩展字段 + + // ========== 基础字段 ========== + private String createBy; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + private String updateBy; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + private String remark; + + // ==================== Getter and Setter ==================== + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public String getUserName() { + return userName; + } + + public void setUserName(String userName) { + this.userName = userName; + } + + public String getNickName() { + return nickName; + } + + public void setNickName(String nickName) { + this.nickName = nickName; + } + + public String getPhonenumber() { + return phonenumber; + } + + public void setPhonenumber(String phonenumber) { + this.phonenumber = phonenumber; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getAvatar() { + return avatar; + } + + public void setAvatar(String avatar) { + this.avatar = avatar; + } + + public String getUserType() { + return userType; + } + + public void setUserType(String userType) { + this.userType = userType; + } + + public String getAreaCode() { + return areaCode; + } + + public void setAreaCode(String areaCode) { + this.areaCode = areaCode; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public Date getLoginDate() { + return loginDate; + } + + public void setLoginDate(Date loginDate) { + this.loginDate = loginDate; + } + + public Long getPersonalInfoId() { + return personalInfoId; + } + + public void setPersonalInfoId(Long personalInfoId) { + this.personalInfoId = personalInfoId; + } + + public String getRealName() { + return realName; + } + + public void setRealName(String realName) { + this.realName = realName; + } + + public String getGender() { + return gender; + } + + public void setGender(String gender) { + this.gender = gender; + } + + public Date getBirthday() { + return birthday; + } + + public void setBirthday(Date birthday) { + this.birthday = birthday; + } + + public String getIdCard() { + return idCard; + } + + public void setIdCard(String idCard) { + this.idCard = idCard; + } + + public String getSpecialty() { + return specialty; + } + + public void setSpecialty(String specialty) { + this.specialty = specialty; + } + + public String getWorkExperience() { + return workExperience; + } + + public void setWorkExperience(String workExperience) { + this.workExperience = workExperience; + } + + public String getHospital() { + return hospital; + } + + public void setHospital(String hospital) { + this.hospital = hospital; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getIphone() { + return iphone; + } + + public void setIphone(String iphone) { + this.iphone = iphone; + } + + public String getIntroduction() { + return introduction; + } + + public void setIntroduction(String introduction) { + this.introduction = introduction; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public String getExpertType() { + return expertType; + } + + public void setExpertType(String expertType) { + this.expertType = expertType; + } + + public String getPersonalEmail() { + return personalEmail; + } + + public void setPersonalEmail(String personalEmail) { + this.personalEmail = personalEmail; + } + + public String getPersonalNickName() { + return personalNickName; + } + + public void setPersonalNickName(String personalNickName) { + this.personalNickName = personalNickName; + } + + public Long getQualificationId() { + return qualificationId; + } + + public void setQualificationId(Long qualificationId) { + this.qualificationId = qualificationId; + } + + public String getQualificationType() { + return qualificationType; + } + + public void setQualificationType(String qualificationType) { + this.qualificationType = qualificationType; + } + + public String getQualificationTypeLabel() { + return qualificationTypeLabel; + } + + public void setQualificationTypeLabel(String qualificationTypeLabel) { + this.qualificationTypeLabel = qualificationTypeLabel; + } + + public String getScopeIds() { + return scopeIds; + } + + public void setScopeIds(String scopeIds) { + this.scopeIds = scopeIds; + } + + public String getScopeNames() { + return scopeNames; + } + + public void setScopeNames(String scopeNames) { + this.scopeNames = scopeNames; + } + + public List getCertificates() { + return certificates; + } + + public void setCertificates(List certificates) { + this.certificates = certificates; + } + + public Integer getRemindDays() { + return remindDays; + } + + public void setRemindDays(Integer remindDays) { + this.remindDays = remindDays; + } + + public String getCertName() { + return certName; + } + + public void setCertName(String certName) { + this.certName = certName; + } + + public String getCertificateNo() { + return certificateNo; + } + + public void setCertificateNo(String certificateNo) { + this.certificateNo = certificateNo; + } + + public String getIssueOrg() { + return issueOrg; + } + + public void setIssueOrg(String issueOrg) { + this.issueOrg = issueOrg; + } + + public String getCertificateFiles() { + return certificateFiles; + } + + public void setCertificateFiles(String certificateFiles) { + this.certificateFiles = certificateFiles; + } + + public Date getIssueDate() { + return issueDate; + } + + public void setIssueDate(Date issueDate) { + this.issueDate = issueDate; + } + + public Date getExpireDate() { + return expireDate; + } + + public void setExpireDate(Date expireDate) { + this.expireDate = expireDate; + } + + public String getCertStatus() { + return certStatus; + } + + public void setCertStatus(String certStatus) { + this.certStatus = certStatus; + } + + public String getCertStatusLabel() { + return certStatusLabel; + } + + public void setCertStatusLabel(String certStatusLabel) { + this.certStatusLabel = certStatusLabel; + } + + public Long getCertId() { + return certId; + } + + public void setCertId(Long certId) { + this.certId = certId; + } + + public String getCertificatesJson() { + return certificatesJson; + } + + public void setCertificatesJson(String certificatesJson) { + this.certificatesJson = certificatesJson; + } + + public String getAuditStatus() { + return auditStatus; + } + + public void setAuditStatus(String auditStatus) { + this.auditStatus = auditStatus; + } + + public String getAuditStatusLabel() { + return auditStatusLabel; + } + + public void setAuditStatusLabel(String auditStatusLabel) { + this.auditStatusLabel = auditStatusLabel; + } + + public String getAuditOpinion() { + return auditOpinion; + } + + public void setAuditOpinion(String auditOpinion) { + this.auditOpinion = auditOpinion; + } + + public Date getApplyTime() { + return applyTime; + } + + public void setApplyTime(Date applyTime) { + this.applyTime = applyTime; + } + + public Date getAuditTime() { + return auditTime; + } + + public void setAuditTime(Date auditTime) { + this.auditTime = auditTime; + } + + public Long getAuditorId() { + return auditorId; + } + + public void setAuditorId(Long auditorId) { + this.auditorId = auditorId; + } + + public String getAuditor() { + return auditor; + } + + public void setAuditor(String auditor) { + this.auditor = auditor; + } + + public String getAuthStatus() { + return authStatus; + } + + public void setAuthStatus(String authStatus) { + this.authStatus = authStatus; + } + + public Date getAuthTime() { + return authTime; + } + + public void setAuthTime(Date authTime) { + this.authTime = authTime; + } + + public String getMaskedIdCard() { + return maskedIdCard; + } + + public void setMaskedIdCard(String maskedIdCard) { + this.maskedIdCard = maskedIdCard; + } + + public Integer getTotalCertificates() { + return totalCertificates; + } + + public void setTotalCertificates(Integer totalCertificates) { + this.totalCertificates = totalCertificates; + } + + public Integer getExpiringCount() { + return expiringCount; + } + + public void setExpiringCount(Integer expiringCount) { + this.expiringCount = expiringCount; + } + + public Integer getExpiredCount() { + return expiredCount; + } + + public void setExpiredCount(Integer expiredCount) { + this.expiredCount = expiredCount; + } + + public Integer getNormalCount() { + return normalCount; + } + + public void setNormalCount(Integer normalCount) { + this.normalCount = normalCount; + } + + public Integer getCompleteness() { + return completeness; + } + + public void setCompleteness(Integer completeness) { + this.completeness = completeness; + } + + public Boolean getCanSubmitAudit() { + return canSubmitAudit; + } + + public void setCanSubmitAudit(Boolean canSubmitAudit) { + this.canSubmitAudit = canSubmitAudit; + } + + public Map getStatistics() { + return statistics; + } + + public void setStatistics(Map statistics) { + this.statistics = statistics; + } + + public Boolean getHasPersonalInfo() { + return hasPersonalInfo; + } + + public void setHasPersonalInfo(Boolean hasPersonalInfo) { + this.hasPersonalInfo = hasPersonalInfo; + } + + public Boolean getHasQualification() { + return hasQualification; + } + + public void setHasQualification(Boolean hasQualification) { + this.hasQualification = hasQualification; + } + + public Boolean getHasCertificates() { + return hasCertificates; + } + + public void setHasCertificates(Boolean hasCertificates) { + this.hasCertificates = hasCertificates; + } + + public String getWarningLevel() { + return warningLevel; + } + + public void setWarningLevel(String warningLevel) { + this.warningLevel = warningLevel; + } + + public Map getExtra() { + return extra; + } + + public void setExtra(Map extra) { + this.extra = extra; + } + + public String getCreateBy() { + return createBy; + } + + public void setCreateBy(String createBy) { + this.createBy = createBy; + } + + public Date getCreateTime() { + return createTime; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + public String getUpdateBy() { + return updateBy; + } + + public void setUpdateBy(String updateBy) { + this.updateBy = updateBy; + } + + public Date getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(Date updateTime) { + this.updateTime = updateTime; + } + + public String getRemark() { + return remark; + } + + public void setRemark(String remark) { + this.remark = remark; + } +} diff --git a/chenhai-system/src/main/java/com/chenhai/vet/mapper/VetQualificationMapper.java b/chenhai-system/src/main/java/com/chenhai/vet/mapper/VetQualificationMapper.java index d5b8242..bbb9521 100644 --- a/chenhai-system/src/main/java/com/chenhai/vet/mapper/VetQualificationMapper.java +++ b/chenhai-system/src/main/java/com/chenhai/vet/mapper/VetQualificationMapper.java @@ -88,4 +88,5 @@ public interface VetQualificationMapper * @return 兽医资质 */ public VetQualification selectVetQualificationByUserId(@Param("userId") Long userId); -} \ No newline at end of file + +} diff --git a/chenhai-system/src/main/java/com/chenhai/vet/service/IVetUnifiedInfoService.java b/chenhai-system/src/main/java/com/chenhai/vet/service/IVetUnifiedInfoService.java new file mode 100644 index 0000000..8bf09d9 --- /dev/null +++ b/chenhai-system/src/main/java/com/chenhai/vet/service/IVetUnifiedInfoService.java @@ -0,0 +1,52 @@ +package com.chenhai.vet.service; + +import com.chenhai.vet.domain.dto.VetUnifiedInfoDTO; +import java.util.List; +import java.util.Map; + +/** + * 兽医统一信息Service接口 + */ +public interface IVetUnifiedInfoService { + + /** + * 获取当前登录用户的完整信息 + */ + VetUnifiedInfoDTO getCurrentUserInfo(); + + /** + * 根据用户ID获取完整信息 + */ + VetUnifiedInfoDTO getUserInfoByUserId(Long userId); + + /** + * 保存/更新兽医信息 + */ + VetUnifiedInfoDTO saveVetInfo(Map requestData); + + /** + * 提交审核 + */ + boolean submitForAudit(Long userId); + + /** + * 获取统计信息 + */ + Map getStatistics(Long userId); + + /** + * 检查是否需要填写资质 + */ + Map checkNeedQualification(Long userId); + + /** + * 分页查询兽医信息列表(管理员用) + * 分页参数由 MyBatis 分页插件自动处理 + */ + List listVetInfo(VetUnifiedInfoDTO query); + + /** + * 审核兽医信息 + */ + boolean auditVetInfo(Long userId, String auditStatus, String auditOpinion, Long auditorId); +} diff --git a/chenhai-system/src/main/java/com/chenhai/vet/service/impl/VetUnifiedInfoServiceImpl.java b/chenhai-system/src/main/java/com/chenhai/vet/service/impl/VetUnifiedInfoServiceImpl.java new file mode 100644 index 0000000..2eb11b6 --- /dev/null +++ b/chenhai-system/src/main/java/com/chenhai/vet/service/impl/VetUnifiedInfoServiceImpl.java @@ -0,0 +1,1179 @@ +package com.chenhai.vet.service.impl; + +import com.chenhai.common.core.domain.entity.SysUser; +import com.chenhai.common.core.domain.model.LoginUser; +import com.chenhai.common.core.redis.RedisCache; +import com.chenhai.common.exception.ServiceException; +import com.chenhai.common.utils.DateUtils; +import com.chenhai.common.utils.SecurityUtils; +import com.chenhai.common.utils.StringUtils; +import com.chenhai.system.domain.SysMuhuUser; +import com.chenhai.system.service.ISysMuhuUserService; +import com.chenhai.system.service.ISysUserService; +import com.chenhai.vet.domain.VetPersonalInfo; +import com.chenhai.vet.domain.VetQualification; +import com.chenhai.vet.domain.dto.VetUnifiedInfoDTO; +import com.chenhai.vet.mapper.VetPersonalInfoMapper; +import com.chenhai.vet.mapper.VetQualificationMapper; +import com.chenhai.vet.service.IVetUnifiedInfoService; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 兽医统一信息Service实现类 + */ +@Service +public class VetUnifiedInfoServiceImpl implements IVetUnifiedInfoService { + + private static final Logger log = LoggerFactory.getLogger(VetUnifiedInfoServiceImpl.class); + + @Autowired + private VetPersonalInfoMapper vetPersonalInfoMapper; + + @Autowired + private VetQualificationMapper vetQualificationMapper; + + @Autowired + private ISysMuhuUserService sysMuhuUserService; + + @Autowired + private ISysUserService sysUserService; + + @Autowired + private JdbcTemplate jdbcTemplate; + + @Autowired + private RedisCache redisCache; + + @Autowired + private ObjectMapper objectMapper; + + private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); + + @Override + public VetUnifiedInfoDTO getCurrentUserInfo() { + Long userId = SecurityUtils.getUserId(); + if (userId == null) { + throw new ServiceException("用户未登录"); + } + return buildUnifiedInfo(userId); + } + + @Override + public VetUnifiedInfoDTO getUserInfoByUserId(Long userId) { + return buildUnifiedInfo(userId); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public VetUnifiedInfoDTO saveVetInfo(Map requestData) { + Long userId = SecurityUtils.getUserId(); + String username = SecurityUtils.getUsername(); + + // 1. 基础数据校验 + validateRequestData(requestData); + + // 2. 保存个人信息 + savePersonalInfo(userId, username, requestData); + + // 3. 保存资质信息 + saveQualification(userId, username, requestData); + +// // 4. 保存认证信息 +// saveAuthInfo(userId, requestData); + + // 5. 返回更新后的信息 + return buildUnifiedInfo(userId); + } + + @Override + public boolean submitForAudit(Long userId) { + // 获取完整信息检查是否可提交 + VetUnifiedInfoDTO info = buildUnifiedInfo(userId); +// if (!canSubmitAudit(info)) { +// throw new ServiceException("请先完善所有必填信息(姓名、身份证、证书、资质类型、经营范围)"); +// } + + // 更新资质审核状态 + VetQualification qualification = vetQualificationMapper.selectVetQualificationByUserId(userId); + if (qualification == null) { + qualification = new VetQualification(); + qualification.setUserId(userId); + qualification.setRealName(info.getRealName()); + qualification.setIdCard(info.getIdCard()); + qualification.setQualificationType(info.getQualificationType()); + qualification.setScopeIds(info.getScopeIds()); + qualification.setCreateBy(SecurityUtils.getUsername()); + } + + qualification.setAuditStatus("0"); // 待审核 + qualification.setApplyTime(new Date()); + qualification.setUpdateBy(SecurityUtils.getUsername()); + + int result; + if (qualification.getQualificationId() != null) { + result = vetQualificationMapper.updateVetQualification(qualification); + } else { + // 处理证书列表 + if (info.getCertificates() != null && !info.getCertificates().isEmpty()) { + qualification.setCertificateList(info.getCertificates()); + } + result = vetQualificationMapper.insertVetQualification(qualification); + } + + return result > 0; + } + + @Override + public Map getStatistics(Long userId) { + Map statistics = new HashMap<>(); + + // 1. 获取资质统计 + VetQualification qualification = vetQualificationMapper.selectVetQualificationByUserId(userId); + if (qualification != null && qualification.getCertificateList() != null) { + List certificates = qualification.getCertificateList(); + Date today = new Date(); + + Calendar cal = Calendar.getInstance(); + cal.setTime(today); + cal.add(Calendar.DAY_OF_YEAR, 30); + Date thirtyDaysLater = cal.getTime(); + + int total = certificates.size(); + int normal = 0, expiring = 0, expired = 0; + + for (VetQualification.CertificateInfo cert : certificates) { + if (cert.getExpireDate() != null) { + if (cert.getExpireDate().before(today)) { + expired++; + } else if (cert.getExpireDate().before(thirtyDaysLater)) { + expiring++; + } else { + normal++; + } + } else { + normal++; + } + } + + statistics.put("totalCertificates", total); + statistics.put("normal", normal); + statistics.put("expiring", expiring); + statistics.put("expired", expired); + + // 设置警告级别 + if (expired > 0) { + statistics.put("warningLevel", "DANGER"); + } else if (expiring > 0) { + statistics.put("warningLevel", "WARNING"); + } else { + statistics.put("warningLevel", "NORMAL"); + } + } else { + statistics.put("totalCertificates", 0); + statistics.put("normal", 0); + statistics.put("expiring", 0); + statistics.put("expired", 0); + statistics.put("warningLevel", "NORMAL"); + } + + // 2. 获取个人信息完整度 + VetUnifiedInfoDTO info = buildUnifiedInfo(userId); + statistics.put("completeness", calculateCompleteness(info)); + statistics.put("authStatus", info.getAuthStatus()); + statistics.put("auditStatus", info.getAuditStatus()); + statistics.put("realName", info.getRealName()); + statistics.put("hasPersonalInfo", info.getHasPersonalInfo()); + statistics.put("hasQualification", info.getHasQualification()); + + return statistics; + } + + @Override + public Map checkNeedQualification(Long userId) { + // 构建完整信息 + VetUnifiedInfoDTO info = buildUnifiedInfo(userId); + + Map result = new HashMap<>(); + + // ========== 用户基本信息 ========== + result.put("userId", userId); + result.put("userName", info.getUserName()); + result.put("nickName", info.getNickName()); + result.put("phonenumber", info.getPhonenumber()); + result.put("email", info.getEmail()); + result.put("avatar", info.getAvatar()); + result.put("userType", info.getUserType()); + result.put("areaCode", info.getAreaCode()); + result.put("status", info.getStatus()); + + // ========== 兽医个人信息 ========== + result.put("personalInfoId", info.getPersonalInfoId()); + result.put("realName", info.getRealName()); + result.put("idCard", info.getIdCard()); + result.put("phone", info.getPhone()); + result.put("gender", info.getGender()); + result.put("birthday", info.getBirthday()); + result.put("address", info.getAddress()); + result.put("hospital", info.getHospital()); + result.put("title", info.getTitle()); + result.put("specialty", info.getSpecialty()); + result.put("workExperience", info.getWorkExperience()); + result.put("expertType", info.getExpertType()); + result.put("introduction", info.getIntroduction()); + result.put("personalEmail", info.getPersonalEmail()); + result.put("personalNickName", info.getPersonalNickName()); + + // ========== 兽医资质信息 ========== + result.put("qualificationId", info.getQualificationId()); + result.put("qualificationType", info.getQualificationType()); + result.put("qualificationTypeLabel", info.getQualificationTypeLabel()); + result.put("scopeIds", info.getScopeIds()); + result.put("scopeNames", info.getScopeNames()); + result.put("certificates", info.getCertificates()); + result.put("remindDays", info.getRemindDays()); + + // 主证书信息 + result.put("certName", info.getCertName()); + result.put("certificateNo", info.getCertificateNo()); + result.put("issueOrg", info.getIssueOrg()); + result.put("certificateFiles", info.getCertificateFiles()); + result.put("issueDate", info.getIssueDate()); + result.put("expireDate", info.getExpireDate()); + result.put("certStatus", info.getCertStatus()); + result.put("certStatusLabel", info.getCertStatusLabel()); + result.put("certId", info.getCertId()); + + // ========== 审核状态 ========== + result.put("auditStatus", info.getAuditStatus()); + result.put("auditStatusLabel", info.getAuditStatusLabel()); + result.put("auditOpinion", info.getAuditOpinion()); + result.put("applyTime", info.getApplyTime()); + result.put("auditTime", info.getAuditTime()); + + // ========== 认证状态 ========== + result.put("authStatus", info.getAuthStatus()); + result.put("authTime", info.getAuthTime()); + result.put("maskedIdCard", info.getMaskedIdCard()); + + // ========== 统计信息 ========== + result.put("totalCertificates", info.getTotalCertificates()); + result.put("expiringCount", info.getExpiringCount()); + result.put("expiredCount", info.getExpiredCount()); + result.put("normalCount", info.getNormalCount()); + result.put("completeness", info.getCompleteness()); + result.put("warningLevel", info.getWarningLevel()); + + // ========== 辅助字段 ========== + result.put("hasPersonalInfo", info.getHasPersonalInfo()); + result.put("hasQualification", info.getHasQualification()); + result.put("hasCertificates", info.getHasCertificates()); + result.put("canSubmitAudit", info.getCanSubmitAudit()); + + // ========== 弹窗判断 ========== + boolean needPopup = !info.getHasPersonalInfo() || !info.getHasQualification() + || !info.getHasCertificates() || StringUtils.isEmpty(info.getAuditStatus()) + || "2".equals(info.getAuditStatus()); + + result.put("needPopup", needPopup); + + if (!info.getHasPersonalInfo()) { + result.put("message", "请先完善个人信息"); + } else if (!info.getHasQualification()) { + result.put("message", "请先填写资质信息"); + } else if (!info.getHasCertificates()) { + result.put("message", "请至少添加一个证书"); + } else if (StringUtils.isEmpty(info.getAuditStatus())) { + result.put("message", "请提交资质审核"); + } else if ("0".equals(info.getAuditStatus())) { + result.put("message", "您的资质正在审核中,请耐心等待"); + } else if ("1".equals(info.getAuditStatus())) { + result.put("message", "您的资质已审核通过"); + } else if ("2".equals(info.getAuditStatus())) { + result.put("message", "您的资质审核不通过:" + (info.getAuditOpinion() != null ? info.getAuditOpinion() : "请重新提交")); + } + + // 添加时间戳 + result.put("timestamp", System.currentTimeMillis()); + + return result; + } + + @Override + public List listVetInfo(VetUnifiedInfoDTO query) { + // 转换为VetPersonalInfo查询条件 + VetPersonalInfo personalQuery = new VetPersonalInfo(); + if (query != null) { + BeanUtils.copyProperties(query, personalQuery); + // 确保 userId 被正确传递 + if (query.getUserId() != null) { + personalQuery.setUserId(query.getUserId()); + } + } + + // MyBatis 分页插件会自动处理分页 + List personalList = vetPersonalInfoMapper.selectVetPersonalInfoList(personalQuery); + + // 转换为统一DTO + return personalList.stream() + .map(info -> { + VetUnifiedInfoDTO dto = new VetUnifiedInfoDTO(); + BeanUtils.copyProperties(info, dto); + dto.setUserId(info.getUserId()); + dto.setPersonalInfoId(info.getId()); + dto.setPersonalEmail(info.getEmail()); + dto.setPersonalNickName(info.getNickName()); + + // 从关联的用户对象获取用户基本信息 + SysUser user = info.getUser(); + if (user != null) { + dto.setUserName(user.getUserName()); + dto.setNickName(user.getNickName()); + dto.setPhonenumber(user.getPhonenumber()); + dto.setEmail(user.getEmail()); + dto.setAvatar(user.getAvatar()); + dto.setUserType(user.getUserType()); + dto.setAreaCode(user.getAreaCode()); + dto.setStatus(user.getStatus()); + } + + // 补充资质信息 + VetQualification qualification = vetQualificationMapper.selectVetQualificationByUserId(info.getUserId()); + if (qualification != null) { + dto.setQualificationId(qualification.getQualificationId()); + dto.setQualificationType(qualification.getQualificationType()); + dto.setQualificationTypeLabel(qualification.getQualificationTypeLabel()); + dto.setScopeIds(qualification.getScopeIds()); + dto.setScopeNames(qualification.getScopeNames()); + dto.setCertificates(qualification.getCertificateList()); + dto.setAuditStatus(qualification.getAuditStatus()); + dto.setAuditStatusLabel(qualification.getAuditStatusLabel()); + dto.setApplyTime(qualification.getApplyTime()); + dto.setAuditTime(qualification.getAuditTime()); + dto.setAuditOpinion(qualification.getAuditOpinion()); + + dto.setHasQualification(true); + dto.setHasCertificates(qualification.getCertificateList() != null && !qualification.getCertificateList().isEmpty()); + } else { + dto.setHasQualification(false); + dto.setHasCertificates(false); + } + + dto.setHasPersonalInfo(true); + dto.setCompleteness(calculateCompleteness(dto)); + + return dto; + }) + .collect(Collectors.toList()); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean auditVetInfo(Long userId, String auditStatus, String auditOpinion, Long auditorId) { + // 1. 更新资质审核状态 + VetQualification qualification = vetQualificationMapper.selectVetQualificationByUserId(userId); + if (qualification == null) { + throw new ServiceException("该用户没有资质信息"); + } + + String oldStatus = qualification.getAuditStatus(); + + qualification.setAuditStatus(auditStatus); + qualification.setAuditOpinion(auditOpinion); + qualification.setAuditTime(new Date()); + qualification.setAuditorId(auditorId); + qualification.setUpdateBy(SecurityUtils.getUsername()); + + int result = vetQualificationMapper.updateVetQualification(qualification); + +// // 2. 处理角色变更 +// if (result > 0) { +// handleRoleChangeOnAudit(userId, oldStatus, auditStatus); +// } + + return result > 0; + } + + // ==================== 私有辅助方法 ==================== + + /** + * 构建统一信息对象 + */ + private VetUnifiedInfoDTO buildUnifiedInfo(Long userId) { + VetUnifiedInfoDTO dto = new VetUnifiedInfoDTO(); + dto.setUserId(userId); + + // 1. 获取用户基本信息(从sys_user表) + SysUser user = sysUserService.selectUserById(userId); + if (user != null) { + dto.setUserName(user.getUserName()); + dto.setNickName(user.getNickName()); // 使用sys_user的nickname + dto.setPhonenumber(user.getPhonenumber()); + dto.setEmail(user.getEmail()); + dto.setAvatar(user.getAvatar()); // 使用sys_user的avatar + dto.setUserType(user.getUserType()); + dto.setAreaCode(user.getAreaCode()); + dto.setStatus(user.getStatus()); + dto.setLoginDate(user.getLoginDate()); + } + + // 2. 获取兽医个人信息 + VetPersonalInfo personalInfo = vetPersonalInfoMapper.selectVetPersonalInfoByUserId(userId); + if (personalInfo != null) { + // 使用BeanUtils.copyProperties,但排除nickName和avatar字段 + // 这样就不会覆盖上面从sys_user获取的值 + BeanUtils.copyProperties(personalInfo, dto, + "nickName", // 排除nickName,使用sys_user的值 + "avatar"); // 排除avatar,使用sys_user的值 + + dto.setPersonalInfoId(personalInfo.getId()); + dto.setPersonalEmail(personalInfo.getEmail()); + dto.setPersonalNickName(personalInfo.getNickName()); // 兽医个人表的昵称放到单独字段 + dto.setHasPersonalInfo(true); + } else { + dto.setHasPersonalInfo(false); + } + + // 3. 获取资质信息 + VetQualification qualification = vetQualificationMapper.selectVetQualificationByUserId(userId); + if (qualification != null) { + dto.setQualificationId(qualification.getQualificationId()); + dto.setQualificationType(qualification.getQualificationType()); + dto.setQualificationTypeLabel(qualification.getQualificationTypeLabel()); + dto.setScopeIds(qualification.getScopeIds()); + dto.setScopeNames(qualification.getScopeNames()); + dto.setCertificates(qualification.getCertificateList()); + dto.setRemindDays(qualification.getRemindDays()); + + // 主证书信息 + dto.setCertName(qualification.getCertName()); + dto.setCertificateNo(qualification.getCertificateNo()); + dto.setIssueOrg(qualification.getIssueOrg()); + dto.setCertificateFiles(qualification.getCertificateFiles()); + dto.setIssueDate(qualification.getIssueDate()); + dto.setExpireDate(qualification.getExpireDate()); + dto.setCertStatus(qualification.getCertStatus()); + dto.setCertStatusLabel(qualification.getCertStatusLabel()); + dto.setCertId(qualification.getCertId()); + dto.setCertificatesJson(qualification.getCertificatesJson()); + + // 审核状态 + dto.setAuditStatus(qualification.getAuditStatus()); + dto.setAuditStatusLabel(qualification.getAuditStatusLabel()); + dto.setAuditOpinion(qualification.getAuditOpinion()); + dto.setApplyTime(qualification.getApplyTime()); + dto.setAuditTime(qualification.getAuditTime()); + dto.setAuditorId(qualification.getAuditorId()); + + dto.setHasQualification(true); + dto.setHasCertificates(qualification.getCertificateList() != null && !qualification.getCertificateList().isEmpty()); + + // 统计证书 + if (qualification.getCertificateList() != null) { + calculateCertificateStats(dto, qualification.getCertificateList()); + } + } else { + dto.setHasQualification(false); + dto.setHasCertificates(false); + dto.setTotalCertificates(0); + dto.setNormalCount(0); + dto.setExpiringCount(0); + dto.setExpiredCount(0); + dto.setWarningLevel("NORMAL"); + } + + // 4. 获取认证信息 + SysMuhuUser muhuUser = sysMuhuUserService.selectSysMuhuUserByUserId(userId); + if (muhuUser != null) { + dto.setAuthStatus(muhuUser.getAuthStatus()); + dto.setAuthTime(muhuUser.getAuthTime()); + + // 身份证脱敏 + if (StringUtils.isNotEmpty(muhuUser.getIdCard()) && muhuUser.getIdCard().length() >= 15) { + String idCard = muhuUser.getIdCard(); + String masked = idCard.substring(0, 6) + "********" + idCard.substring(idCard.length() - 4); + dto.setMaskedIdCard(masked); + } + + // 如果muhu表有实名信息但personalInfo没有,则补充 + if (personalInfo == null || StringUtils.isEmpty(dto.getRealName())) { + dto.setRealName(muhuUser.getRealName()); + dto.setIdCard(muhuUser.getIdCard()); + } + } + + // 5. 计算信息完整度 + dto.setCompleteness(calculateCompleteness(dto)); + dto.setCanSubmitAudit(canSubmitAudit(dto)); + + // 6. 设置基础字段 + if (personalInfo != null) { + dto.setCreateBy(personalInfo.getCreateBy()); + dto.setCreateTime(personalInfo.getCreateTime()); + dto.setUpdateBy(personalInfo.getUpdateBy()); + dto.setUpdateTime(personalInfo.getUpdateTime()); + } + + // 添加日志,方便调试 + log.debug("buildUnifiedInfo - userId: {}, nickName: {}, avatar: {}", + userId, dto.getNickName(), dto.getAvatar()); + + return dto; + } + + /** + * 计算证书统计 + */ + private void calculateCertificateStats(VetUnifiedInfoDTO dto, List certificates) { + Date today = new Date(); + Calendar cal = Calendar.getInstance(); + cal.setTime(today); + cal.add(Calendar.DAY_OF_YEAR, 30); + Date thirtyDaysLater = cal.getTime(); + + int total = certificates.size(); + int normal = 0, expiring = 0, expired = 0; + + for (VetQualification.CertificateInfo cert : certificates) { + if (cert.getExpireDate() != null) { + if (cert.getExpireDate().before(today)) { + expired++; + cert.setCertStatus("2"); + cert.setCertStatusLabel("已过期"); + } else if (cert.getExpireDate().before(thirtyDaysLater)) { + expiring++; + cert.setCertStatus("1"); + cert.setCertStatusLabel("即将过期"); + } else { + normal++; + cert.setCertStatus("0"); + cert.setCertStatusLabel("正常"); + } + } else { + normal++; + cert.setCertStatus("0"); + cert.setCertStatusLabel("正常"); + } + } + + dto.setTotalCertificates(total); + dto.setNormalCount(normal); + dto.setExpiringCount(expiring); + dto.setExpiredCount(expired); + + // 设置警告级别 + if (expired > 0) { + dto.setWarningLevel("DANGER"); + } else if (expiring > 0) { + dto.setWarningLevel("WARNING"); + } else { + dto.setWarningLevel("NORMAL"); + } + } + + /** + * 计算信息完整度 + */ + private int calculateCompleteness(VetUnifiedInfoDTO info) { + int total = 0; + int completed = 0; + + // 基础信息 (8项) + total += 8; + if (StringUtils.isNotEmpty(info.getRealName())) completed++; + if (StringUtils.isNotEmpty(info.getIdCard())) completed++; + if (StringUtils.isNotEmpty(info.getPhone())) completed++; + if (StringUtils.isNotEmpty(info.getNickName())) completed++; + if (StringUtils.isNotEmpty(info.getGender())) completed++; + if (info.getBirthday() != null) completed++; + if (StringUtils.isNotEmpty(info.getAddress())) completed++; + if (StringUtils.isNotEmpty(info.getHospital())) completed++; + + // 专业信息 (5项) + total += 5; + if (StringUtils.isNotEmpty(info.getSpecialty())) completed++; + if (StringUtils.isNotEmpty(info.getWorkExperience())) completed++; + if (StringUtils.isNotEmpty(info.getExpertType())) completed++; + if (StringUtils.isNotEmpty(info.getTitle())) completed++; + if (StringUtils.isNotEmpty(info.getIntroduction())) completed++; + + // 资质信息 (3项) + total += 3; + if (info.getHasCertificates()) completed++; + if (StringUtils.isNotEmpty(info.getQualificationType())) completed++; + if (StringUtils.isNotEmpty(info.getScopeIds())) completed++; + + return total > 0 ? (int) ((completed * 100.0) / total) : 0; + } + + /** + * 判断是否可以提交审核 + */ + private boolean canSubmitAudit(VetUnifiedInfoDTO info) { + return StringUtils.isNotEmpty(info.getRealName()) + && StringUtils.isNotEmpty(info.getIdCard()) + && info.getHasCertificates() + && StringUtils.isNotEmpty(info.getQualificationType()) + && StringUtils.isNotEmpty(info.getScopeIds()); + } + + /** + * 校验请求数据 + */ + private void validateRequestData(Map data) { + // 姓名校验 + String realName = (String) data.get("realName"); + if (realName != null && !realName.trim().isEmpty()) { + realName = realName.trim(); + if (!realName.matches("^[\\u4e00-\\u9fa5]{2,20}$")) { + throw new ServiceException("姓名必须是2-20个汉字"); + } + } + + // 身份证校验 + String idCard = (String) data.get("idCard"); + if (idCard != null && !idCard.trim().isEmpty()) { + idCard = idCard.trim().toUpperCase(); + if (!isValidIdCard(idCard)) { + throw new ServiceException("身份证号格式不正确"); + } + } + + // 手机号校验 + String phone = (String) data.get("phone"); + if (phone != null && !phone.trim().isEmpty()) { + if (!isValidPhone(phone.trim())) { + throw new ServiceException("手机号格式不正确"); + } + } + + // 邮箱校验 + String email = (String) data.get("email"); + if (email != null && !email.trim().isEmpty()) { + if (!isValidEmail(email.trim())) { + throw new ServiceException("邮箱格式不正确"); + } + } + } + + /** + * 保存个人信息 + */ + private void savePersonalInfo(Long userId, String username, Map data) { + VetPersonalInfo personalInfo = null; + + // 1. 优先从请求中获取指定的ID + Long targetId = null; + if (data.containsKey("personalInfoId")) { + Object idObj = data.get("personalInfoId"); + if (idObj instanceof Number) { + targetId = ((Number) idObj).longValue(); + } else if (idObj instanceof String) { + try { + targetId = Long.parseLong((String) idObj); + } catch (NumberFormatException e) { + log.warn("解析ID失败: {}", idObj); + } + } + } + + // 2. 如果指定了ID,先查询该记录 + if (targetId != null && targetId > 0) { + personalInfo = vetPersonalInfoMapper.selectVetPersonalInfoById(targetId); + if (personalInfo != null && !personalInfo.getUserId().equals(userId)) { + throw new ServiceException("无权修改他人的信息"); + } + } + + // 3. 如果没有找到指定记录,则按用户ID查询 + if (personalInfo == null) { + personalInfo = vetPersonalInfoMapper.selectVetPersonalInfoByUserId(userId); + } + + boolean hasPersonalData = data.containsKey("realName") || data.containsKey("gender") + || data.containsKey("birthday") || data.containsKey("idCard") + || data.containsKey("specialty") || data.containsKey("workExperience") + || data.containsKey("hospital") || data.containsKey("address") + || data.containsKey("iphone") || data.containsKey("introduction") + || data.containsKey("title") || data.containsKey("phone") + || data.containsKey("expertType") || data.containsKey("personalEmail") + || data.containsKey("personalNickName") || data.containsKey("avatar") + || data.containsKey("nickName"); + + if (!hasPersonalData) { + return; + } + + if (personalInfo == null) { + personalInfo = new VetPersonalInfo(); + personalInfo.setUserId(userId); + personalInfo.setCreateBy(username); + } + + // 更新兽医个人信息表的字段 + if (data.containsKey("realName")) personalInfo.setRealName((String) data.get("realName")); + if (data.containsKey("gender")) { + Object genderObj = data.get("gender"); + if (genderObj == null || StringUtils.isEmpty(genderObj.toString())) { + personalInfo.setGender(null); + } else { + String gender = genderObj.toString().trim(); + if ("男".equals(gender)) { + personalInfo.setGender("男"); + } else if ("女".equals(gender)) { + personalInfo.setGender("女"); + } else { + personalInfo.setGender(gender); + } + } + } + if (data.containsKey("birthday")) { + Object birthdayObj = data.get("birthday"); + if (birthdayObj instanceof String) { + try { + personalInfo.setBirthday(dateFormat.parse((String) birthdayObj)); + } catch (Exception e) { + log.warn("解析生日日期失败: {}", birthdayObj); + } + } + } + if (data.containsKey("idCard")) personalInfo.setIdCard((String) data.get("idCard")); + if (data.containsKey("specialty")) personalInfo.setSpecialty((String) data.get("specialty")); + if (data.containsKey("workExperience")) personalInfo.setWorkExperience((String) data.get("workExperience")); + if (data.containsKey("hospital")) personalInfo.setHospital((String) data.get("hospital")); + if (data.containsKey("address")) personalInfo.setAddress((String) data.get("address")); + if (data.containsKey("iphone")) personalInfo.setIphone((String) data.get("iphone")); + if (data.containsKey("introduction")) personalInfo.setIntroduction((String) data.get("introduction")); + if (data.containsKey("title")) personalInfo.setTitle((String) data.get("title")); + if (data.containsKey("phone")) personalInfo.setPhone((String) data.get("phone")); + if (data.containsKey("expertType")) personalInfo.setExpertType((String) data.get("expertType")); + if (data.containsKey("personalEmail")) personalInfo.setEmail((String) data.get("personalEmail")); + if (data.containsKey("personalNickName")) { + personalInfo.setNickName((String) data.get("personalNickName")); + } + if (data.containsKey("avatar")) { + personalInfo.setAvatar((String) data.get("avatar")); + } + + // 先保存兽医个人信息 + personalInfo.setUpdateBy(username); + personalInfo.setUpdateTime(new Date()); + + if (personalInfo.getId() != null) { + vetPersonalInfoMapper.updateVetPersonalInfo(personalInfo); + log.info("兽医个人信息更新成功 - id: {}, userId: {}", personalInfo.getId(), userId); + } else { + personalInfo.setCreateTime(new Date()); + vetPersonalInfoMapper.insertVetPersonalInfo(personalInfo); + log.info("兽医个人信息新增成功 - id: {}, userId: {}", personalInfo.getId(), userId); + } + + // 然后再同步更新用户表的字段(如果有) + // 使用单独的SQL更新,避免触发角色删除 + if (data.containsKey("nickName")) { + String newNickName = (String) data.get("nickName"); + updateUserNickNameOnly(userId, newNickName); + log.info("用户表昵称同步更新 - userId: {}, nickName: {}", userId, newNickName); + } + + if (data.containsKey("avatar")) { + String newAvatar = (String) data.get("avatar"); + updateUserAvatarOnly(userId, newAvatar); + log.info("用户表头像同步更新 - userId: {}, avatar: {}", userId, newAvatar); + } + } + + /** + * 只更新用户表的昵称,不影响角色 + */ + private void updateUserNickNameOnly(Long userId, String nickName) { + try { + String sql = "UPDATE sys_user SET nick_name = ?, update_time = sysdate() WHERE user_id = ?"; + jdbcTemplate.update(sql, nickName, userId); + } catch (Exception e) { + log.error("用户表昵称更新失败", e); + } + } + + /** + * 只更新用户表的头像,不影响角色 + */ + private void updateUserAvatarOnly(Long userId, String avatar) { + try { + String sql = "UPDATE sys_user SET avatar = ?, update_time = sysdate() WHERE user_id = ?"; + jdbcTemplate.update(sql, avatar, userId); + } catch (Exception e) { + log.error("用户表头像更新失败", e); + } + } + + /** + * 更新用户表的昵称 + */ + private void updateUserNickName(Long userId, String nickName) { + try { + // 只更新昵称字段,不更新角色 + jdbcTemplate.update( + "UPDATE sys_user SET nick_name = ?, update_time = sysdate() WHERE user_id = ?", + nickName, userId + ); + log.info("用户表昵称更新成功 - userId: {}, nickName: {}", userId, nickName); + } catch (Exception e) { + log.error("用户表昵称更新失败", e); + } + } + + /** + * 更新用户表的头像 - 修复版本 + */ + private void updateUserAvatar(Long userId, String avatar) { + try { + // 只更新头像字段,不更新角色 + jdbcTemplate.update( + "UPDATE sys_user SET avatar = ?, update_time = sysdate() WHERE user_id = ?", + avatar, userId + ); + log.info("用户表头像更新成功 - userId: {}, avatar: {}", userId, avatar); + } catch (Exception e) { + log.error("用户表头像更新失败", e); + } + } + + /** + * 保存资质信息 + */ + private void saveQualification(Long userId, String username, Map data) { + VetQualification qualification = vetQualificationMapper.selectVetQualificationByUserId(userId); + + boolean hasQualificationData = data.containsKey("qualificationType") + || data.containsKey("scopeIds") + || data.containsKey("certificates") + || data.containsKey("remindDays"); + + if (!hasQualificationData) { + return; // 没有资质数据,不处理 + } + + if (qualification == null) { + qualification = new VetQualification(); + qualification.setUserId(userId); + qualification.setCreateBy(username); + } + + // 更新基础信息 + if (data.containsKey("realName")) qualification.setRealName((String) data.get("realName")); + if (data.containsKey("idCard")) qualification.setIdCard((String) data.get("idCard")); + if (data.containsKey("qualificationType")) qualification.setQualificationType((String) data.get("qualificationType")); + if (data.containsKey("remindDays")) qualification.setRemindDays((Integer) data.get("remindDays")); + + // 处理经营范围 + if (data.containsKey("scopeIds")) { + Object scopeIdsObj = data.get("scopeIds"); + if (scopeIdsObj instanceof List) { + @SuppressWarnings("unchecked") + List scopeIdsList = (List) scopeIdsObj; + qualification.setScopeIds(String.join(",", scopeIdsList)); + } else if (scopeIdsObj instanceof String) { + qualification.setScopeIds((String) scopeIdsObj); + } + + // 获取经营范围名称 + if (StringUtils.isNotEmpty(qualification.getScopeIds())) { + String scopeNames = getScopeNamesFromDict(qualification.getScopeIds()); + qualification.setScopeNames(scopeNames); + } + } + + // 处理证书列表 + if (data.containsKey("certificates")) { + Object certificatesObj = data.get("certificates"); + if (certificatesObj instanceof List) { + @SuppressWarnings("unchecked") + List> certMaps = (List>) certificatesObj; + + List certificateList = new ArrayList<>(); + for (Map certMap : certMaps) { + VetQualification.CertificateInfo cert = new VetQualification.CertificateInfo(); + cert.setCertName((String) certMap.get("certName")); + cert.setCertificateNo((String) certMap.get("certificateNo")); + cert.setIssueOrg((String) certMap.get("issueOrg")); + cert.setCertificateFiles((String) certMap.get("certificateFiles")); + + // 处理日期 + if (certMap.get("issueDate") != null) { + try { + String issueDateStr = certMap.get("issueDate").toString(); + cert.setIssueDate(dateFormat.parse(issueDateStr)); + } catch (Exception e) { + log.warn("解析发证日期失败: {}", certMap.get("issueDate")); + } + } + + if (certMap.get("expireDate") != null) { + try { + String expireDateStr = certMap.get("expireDate").toString(); + cert.setExpireDate(dateFormat.parse(expireDateStr)); + } catch (Exception e) { + log.warn("解析到期日期失败: {}", certMap.get("expireDate")); + } + } + + // 处理证书ID + Long certId = extractLongFromObject(certMap.get("certId")); + if (certId != null && certId > 0) { + cert.setCertificateId(certId); + } else { + cert.setCertificateId(generateCertificateId()); + } + + certificateList.add(cert); + } + + qualification.setCertificateList(certificateList); + } + } + + qualification.setUpdateBy(username); + qualification.setUpdateTime(new Date()); + + if (qualification.getQualificationId() != null) { + vetQualificationMapper.updateVetQualification(qualification); + } else { + qualification.setCreateTime(new Date()); + vetQualificationMapper.insertVetQualification(qualification); + } + } + + /** + * 保存认证信息 - 修复版本 + * 使用 userId 作为主键判断 + */ + private void saveAuthInfo(Long userId, Map data) { + boolean hasAuthData = data.containsKey("realName") && data.containsKey("idCard"); + if (!hasAuthData) { + return; + } + + SysMuhuUser muhuUser = sysMuhuUserService.selectSysMuhuUserByUserId(userId); + + if (muhuUser == null) { + muhuUser = new SysMuhuUser(); + muhuUser.setUserId(userId); + // 设置默认认证状态为"未认证" + muhuUser.setAuthStatus("未认证"); + } + + // 更新实名信息 + muhuUser.setRealName((String) data.get("realName")); + muhuUser.setIdCard((String) data.get("idCard")); + + // 如果之前是未认证状态,提交实名信息后改为"已认证"或"待审核" + // 这里根据你的业务逻辑决定,可以先设为"已认证" + if ("未认证".equals(muhuUser.getAuthStatus())) { + muhuUser.setAuthStatus("已认证"); + muhuUser.setAuthTime(new Date()); + } + + // 使用 userId 判断是新增还是更新 + if (muhuUser.getUserId() != null) { + sysMuhuUserService.updateSysMuhuUser(muhuUser); + log.info("更新认证信息成功 - userId: {}, realName: {}", userId, muhuUser.getRealName()); + } else { + sysMuhuUserService.insertSysMuhuUser(muhuUser); + log.info("新增认证信息成功 - userId: {}, realName: {}", userId, muhuUser.getRealName()); + } + } + + /** + * 从字典表获取经营范围名称 + */ + private String getScopeNamesFromDict(String scopeIds) { + if (StringUtils.isEmpty(scopeIds)) { + return ""; + } + + try { + String[] idArray = scopeIds.split(","); + List cleanedIds = new ArrayList<>(); + for (String id : idArray) { + if (StringUtils.isNotEmpty(id.trim())) { + cleanedIds.add("'" + id.trim() + "'"); + } + } + + if (cleanedIds.isEmpty()) { + return ""; + } + + String inCondition = String.join(",", cleanedIds); + String sql = "SELECT dict_label FROM sys_dict_data " + + "WHERE dict_type = 'business_scope' " + + "AND dict_value IN (" + inCondition + ") " + + "AND status = '0' " + + "ORDER BY FIND_IN_SET(dict_value, ?)"; + + List names = jdbcTemplate.query(sql, + (rs, rowNum) -> rs.getString("dict_label"), + scopeIds); + + return String.join(",", names); + + } catch (Exception e) { + log.error("获取经营范围名称失败, scopeIds: {}", scopeIds, e); + return ""; + } + } + + /** + * 处理审核状态变更时的角色切换 + */ +// private void handleRoleChangeOnAudit(Long userId, String oldStatus, String newStatus) { +// // 如果状态没有变化,不处理 +// if (oldStatus == null || oldStatus.equals(newStatus)) { +// log.info("审核状态未变化,跳过角色切换 - userId: {}, oldStatus: {}, newStatus: {}", userId, oldStatus, newStatus); +// return; +// } +// +// try { +// // 审核通过:状态变为1 +// if ("1".equals(newStatus)) { +// changeRoleToVet(userId); +// } +// // 审核不通过或待审核:状态变为2或0,且之前是通过状态 +// else if (("0".equals(newStatus) || "2".equals(newStatus)) && "1".equals(oldStatus)) { +// changeRoleToVetUnapproved(userId); +// } +// } catch (Exception e) { +// log.error("角色切换失败", e); +// } +// } + + /** + * 切换为兽医角色(审核通过) + */ + private void changeRoleToVet(Long userId) { + try { + // 删除兽医未审核角色 + jdbcTemplate.update("DELETE FROM sys_user_role WHERE user_id = ? AND role_id = 6", userId); + + // 检查是否已有兽医角色 + Integer count = jdbcTemplate.queryForObject( + "SELECT COUNT(*) FROM sys_user_role WHERE user_id = ? AND role_id = 4", + Integer.class, userId); + + if (count == null || count == 0) { + jdbcTemplate.update("INSERT INTO sys_user_role (user_id, role_id) VALUES (?, 4)", userId); + } + + // 刷新用户缓存 + refreshUserCache(userId); + + } catch (Exception e) { + log.error("切换兽医角色失败", e); + } + } + + /** + * 切换为兽医未审核角色 + */ + private void changeRoleToVetUnapproved(Long userId) { + try { + // 删除兽医角色 + jdbcTemplate.update("DELETE FROM sys_user_role WHERE user_id = ? AND role_id = 4", userId); + + // 检查是否已有兽医未审核角色 + Integer count = jdbcTemplate.queryForObject( + "SELECT COUNT(*) FROM sys_user_role WHERE user_id = ? AND role_id = 6", + Integer.class, userId); + + if (count == null || count == 0) { + jdbcTemplate.update("INSERT INTO sys_user_role (user_id, role_id) VALUES (?, 6)", userId); + } + + // 刷新用户缓存 + refreshUserCache(userId); + + } catch (Exception e) { + log.error("切换兽医未审核角色失败", e); + } + } + + /** + * 刷新用户缓存 + */ + private void refreshUserCache(Long userId) { + try { + // 这里可以根据你的缓存实现来刷新 + // 例如清除Redis中的用户权限缓存 + } catch (Exception e) { + log.warn("刷新用户缓存失败", e); + } + } + + /** + * 从对象中提取Long值 + */ + private Long extractLongFromObject(Object obj) { + if (obj == null) { + return null; + } + + try { + if (obj instanceof Number) { + return ((Number) obj).longValue(); + } else if (obj instanceof String) { + String str = ((String) obj).trim(); + if (!str.isEmpty()) { + return Long.parseLong(str); + } + } + } catch (Exception e) { + // 忽略解析异常 + } + + return null; + } + + /** + * 生成安全的证书ID + */ + private Long generateCertificateId() { + long timestamp = System.currentTimeMillis(); + long id = timestamp % 1000000000000L; + id += 100000000L; + if (id > 9007199254740991L) { + id = id % (9007199254740991L - 100000000L) + 100000000L; + } + return id; + } + + // ==================== 校验方法 ==================== + + private boolean isValidIdCard(String idCard) { + if (StringUtils.isEmpty(idCard)) return false; + String regex = "^[1-9]\\d{5}(18|19|20)\\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\\d|3[01])\\d{3}[0-9X]$"; + return idCard.matches(regex); + } + + private boolean isValidPhone(String phone) { + if (StringUtils.isEmpty(phone)) return false; + String regex = "^1[3-9]\\d{9}$"; + return phone.matches(regex); + } + + private boolean isValidEmail(String email) { + if (StringUtils.isEmpty(email)) return false; + String regex = "^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+$"; + return email.matches(regex); + } +} diff --git a/chenhai-ui/src/api/system/user.js b/chenhai-ui/src/api/system/user.js index 3ba364c..647b2ee 100644 --- a/chenhai-ui/src/api/system/user.js +++ b/chenhai-ui/src/api/system/user.js @@ -163,3 +163,11 @@ export function areaTreeSelect(query) { params: query }) } + +// 获取用户信息 +export function getUserInfo() { + return request({ + url: '/muhu/user/getUserInfo', + method: 'get' + }) +} diff --git a/chenhai-ui/src/views/syd.vue b/chenhai-ui/src/views/syd.vue index 640d9b8..ba36ed8 100644 --- a/chenhai-ui/src/views/syd.vue +++ b/chenhai-ui/src/views/syd.vue @@ -254,7 +254,7 @@ - + @@ -539,8 +539,14 @@