package cn.iocoder.yudao.module.ums.service.member; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.archive.api.document.ArchiveDocumentApi; import cn.iocoder.yudao.module.archive.api.document.dto.ArchiveDocumentCreateReqDTO; import cn.iocoder.yudao.module.archive.api.document.dto.ArchiveDocumentRespDTO; import cn.iocoder.yudao.module.system.api.storage.StorageAttachmentApi; import cn.iocoder.yudao.module.system.api.storage.dto.StorageBlobRespDTO; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserCreateReqDTO; import cn.iocoder.yudao.module.ums.controller.admin.member.vo.UmsMemberPageReqVO; import cn.iocoder.yudao.module.ums.controller.admin.member.vo.UmsMemberSaveReqVO; import cn.iocoder.yudao.module.ums.dal.dataobject.application.UmsApplicationDO; import cn.iocoder.yudao.module.ums.dal.dataobject.member.UmsMemberDO; import cn.iocoder.yudao.module.ums.dal.mysql.member.UmsMemberMapper; import cn.iocoder.yudao.module.ums.dal.redis.UmsNoRedisDAO; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import java.time.LocalDate; import java.util.Collections; import java.util.List; import java.util.Set; import java.util.stream.Collectors; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.ums.enums.ErrorCodeConstants.*; @Service @Validated @Slf4j public class UmsMemberServiceImpl implements UmsMemberService { /** * 会员系统账号默认部门 ID(system_dept 中「学会会员」) */ private static final Long MEMBER_DEPT_ID = 129L; public static final String MEMBER_RECORD_TYPE = "ums_member"; /** 会员状态:正常(可归档) */ private static final Integer MEMBER_STATUS_NORMAL = 1; /** 归档来源类型:3-会员(对应 archive 模块 source_type) */ private static final Integer ARCHIVE_SOURCE_TYPE_MEMBER = 3; /** 归档密级:2-内部 */ private static final Integer ARCHIVE_SECRET_LEVEL_INTERNAL = 2; /** 会员档案分类编码(对应 archive_category.code) */ private static final String ARCHIVE_CATEGORY_CODE_MEMBER = "member"; @Resource private UmsMemberMapper memberMapper; @Resource private UmsNoRedisDAO noRedisDAO; @Resource private AdminUserApi adminUserApi; @Resource private StorageAttachmentApi storageAttachmentApi; @Resource private ArchiveDocumentApi archiveDocumentApi; @Override @Transactional(rollbackFor = Exception.class) public UmsMemberDO createMemberFromApplication(UmsApplicationDO application) { // 1. 生成会员编号 String no = noRedisDAO.generate(UmsNoRedisDAO.MEMBER_NO_PREFIX); if (memberMapper.selectByNo(no) != null) { throw exception(MEMBER_NO_EXISTS); } // 2. 创建系统用户账号 Long userId = createSystemUser(application); // 3. 创建会员记录 UmsMemberDO member = UmsMemberDO.builder() .no(no) .userId(userId) .applicationId(application.getId()) .memberType(application.getMemberType()) .memberGrade(application.getMemberGrade()) .name(application.getApplicantName()) .contactPhone(application.getContactPhone()) .contactEmail(application.getContactEmail()) .legalPerson(application.getLegalPerson()) .organizationCode(application.getOrganizationCode()) .researchDirection(application.getResearchDirection()) .workUnit(application.getWorkUnit()) .title(application.getTitle()) .regionCode(application.getRegionCode()) .industry(application.getIndustry()) .joinDate(LocalDate.now()) .status(1) // 正常 .build(); memberMapper.insert(member); return member; } @Override @Transactional(rollbackFor = Exception.class) public Long createMember(UmsMemberSaveReqVO createReqVO) { String no = noRedisDAO.generate(UmsNoRedisDAO.MEMBER_NO_PREFIX); if (memberMapper.selectByNo(no) != null) { throw exception(MEMBER_NO_EXISTS); } // 会员等级与类型匹配校验 validateMemberGrade(createReqVO.getMemberType(), createReqVO.getMemberGrade()); UmsMemberDO member = BeanUtils.toBean(createReqVO, UmsMemberDO.class, in -> in .setNo(no) .setStatus(createReqVO.getStatus() != null ? createReqVO.getStatus() : 1) .setJoinDate(createReqVO.getJoinDate() != null ? createReqVO.getJoinDate() : LocalDate.now())); memberMapper.insert(member); storageAttachmentApi.bindAttachments("file", MEMBER_RECORD_TYPE, member.getId(), createReqVO.getBlobIds()); return member.getId(); } @Override @Transactional(rollbackFor = Exception.class) public void updateMember(UmsMemberSaveReqVO updateReqVO) { validateMemberExists(updateReqVO.getId()); validateMemberGrade(updateReqVO.getMemberType(), updateReqVO.getMemberGrade()); UmsMemberDO updateObj = BeanUtils.toBean(updateReqVO, UmsMemberDO.class); memberMapper.updateById(updateObj); storageAttachmentApi.updateAttachments("file", MEMBER_RECORD_TYPE, updateReqVO.getId(), updateReqVO.getBlobIds()); } @Override @Transactional(rollbackFor = Exception.class) public void deleteMember(Long id) { validateMemberExists(id); memberMapper.deleteById(id); storageAttachmentApi.deleteAttachmentsByRecord(MEMBER_RECORD_TYPE, id); } @Override public UmsMemberDO getMember(Long id) { return memberMapper.selectById(id); } @Override public UmsMemberDO getMemberByUserId(Long userId) { return memberMapper.selectByUserId(userId); } @Override public PageResult getMemberPage(UmsMemberPageReqVO pageReqVO) { return memberMapper.selectPage(pageReqVO); } @Override @Transactional(rollbackFor = Exception.class) public String archiveMember(Long id) { // 1.1 校验会员存在 UmsMemberDO member = validateMemberExists(id); // 1.2 只有「正常」状态的会员才能归档 if (!MEMBER_STATUS_NORMAL.equals(member.getStatus())) { throw exception(MEMBER_ARCHIVE_FAIL_STATUS, member.getNo(), memberStatusName(member.getStatus())); } // 1.3 重复归档校验 if (member.getArchiveDocumentId() != null) { throw exception(MEMBER_ALREADY_ARCHIVED, member.getNo(), StrUtil.blankToDefault(archiveNo(id), "未知")); } // 2. 带入会员材料附件,按来源单据归档 List blobIds = storageAttachmentApi.listAttachments(MEMBER_RECORD_TYPE, id).stream() .map(StorageBlobRespDTO::getId).collect(Collectors.toList()); Long archiveDocumentId = archiveDocumentApi.archiveBySource(new ArchiveDocumentCreateReqDTO() .setSourceType(ARCHIVE_SOURCE_TYPE_MEMBER) .setSourceId(id) .setSourceNo(member.getNo()) .setSourceTitle(member.getName()) .setCategoryCode(ARCHIVE_CATEGORY_CODE_MEMBER) .setSecretLevel(ARCHIVE_SECRET_LEVEL_INTERNAL) .setSummary("会员材料归档") .setBlobIds(blobIds)).getCheckedData(); // 3. 回写归档档案 ID(作为已归档硬标记) memberMapper.updateById(new UmsMemberDO().setId(id).setArchiveDocumentId(archiveDocumentId)); return StrUtil.blankToDefault(archiveNo(id), "未知"); } // =================== 内部方法 =================== private Long createSystemUser(UmsApplicationDO application) { // 手机号可作为用户名/密码(参考 HRM 员工模式) String mobile = StrUtil.blankToDefault(application.getContactPhone(), "unknown"); String username = mobile; String nickname = application.getApplicantName(); try { return adminUserApi.createUser(new AdminUserCreateReqDTO() .setUsername(username) .setNickname(nickname) .setDeptId(MEMBER_DEPT_ID) .setPostIds(Collections.emptySet()) .setMobile(mobile) .setPassword(mobile) // 默认密码为手机号 .setEmail(application.getContactEmail()) .setRemark("由入会申请自动创建:" + application.getNo())); } catch (Exception e) { log.error("[createSystemUser] 创建会员系统账号失败,申请单={}", application.getId(), e); // 如果手机号重复,给出明确提示 if (e.getMessage() != null && e.getMessage().contains("手机号")) { throw exception(MEMBER_USER_MOBILE_EXISTS); } throw exception(MEMBER_USER_CREATE_FAIL); } } /** * 校验会员等级与类型匹配:单位会员(type=2)不可选个人专有等级「5-理事长」 */ private void validateMemberGrade(Integer memberType, String memberGrade) { if (memberType == null || StrUtil.isBlank(memberGrade)) { return; } if (memberType == 2 && "5".equals(memberGrade)) { throw exception(MEMBER_GRADE_NOT_MATCH_TYPE); } } private UmsMemberDO validateMemberExists(Long id) { UmsMemberDO member = memberMapper.selectById(id); if (member == null) { throw exception(MEMBER_NOT_EXISTS); } return member; } /** 会员状态中文名:1-正常 0-暂停(用于归档失败提示) */ private String memberStatusName(Integer status) { if (status == null) { return "未知"; } return MEMBER_STATUS_NORMAL.equals(status) ? "正常" : "暂停"; } /** 查询来源单据已生成的档案编号(未归档时返回 null) */ private String archiveNo(Long memberId) { ArchiveDocumentRespDTO archive = archiveDocumentApi .getBySource(ARCHIVE_SOURCE_TYPE_MEMBER, memberId).getCheckedData(); return archive != null ? archive.getNo() : null; } }