This commit is contained in:
ran.shang
2026-09-09 10:05:23 +08:00
parent 4bd6b57b4f
commit 54b7e9a555
7 changed files with 129 additions and 7 deletions

View File

@@ -24,5 +24,13 @@ public class TransferMetadataDTO implements Serializable {
@ApiModelProperty("扩展用户文件DTO") @ApiModelProperty("扩展用户文件DTO")
private ExpandUserFileDTO expandUserFile; private ExpandUserFileDTO expandUserFile;
} @ApiModelProperty("源端用户ID,仅用于日志追踪")
private Long sourceUserId;
@ApiModelProperty("源端用户手机号")
private String sourceUserPhone;
@ApiModelProperty("源端用户类型")
private Integer sourceUserType;
}

View File

@@ -7,17 +7,17 @@ import com.senmesh.component.file.config.FileProperties;
import com.senmesh.component.file.service.FileService; import com.senmesh.component.file.service.FileService;
import com.senmesh.sfile.constant.FileLocationEnum; import com.senmesh.sfile.constant.FileLocationEnum;
import com.senmesh.sfile.constant.FileStatusEnum; import com.senmesh.sfile.constant.FileStatusEnum;
import com.senmesh.sfile.constant.FileTypeEnum;
import com.senmesh.sfile.constant.RootTypeEnum; import com.senmesh.sfile.constant.RootTypeEnum;
import com.senmesh.sfile.dto.ExpandUserFileDTO; import com.senmesh.sfile.dto.ExpandUserFileDTO;
import com.senmesh.sfile.dto.FileRiskDTO; import com.senmesh.sfile.dto.FileRiskDTO;
import com.senmesh.sfile.dto.TransferMetadataDTO; import com.senmesh.sfile.dto.TransferMetadataDTO;
import com.senmesh.sfile.dto.UserFileCreateDTO;
import com.senmesh.sfile.dto.UserFileDTO; import com.senmesh.sfile.dto.UserFileDTO;
import com.senmesh.sfile.dto.UserFileModifyStatusDTO; import com.senmesh.sfile.dto.UserFileModifyStatusDTO;
import com.senmesh.sfile.service.FileRiskService; import com.senmesh.sfile.service.FileRiskService;
import com.senmesh.sfile.service.TransferFileMonitorService; import com.senmesh.sfile.service.TransferFileMonitorService;
import com.senmesh.sfile.service.UserFileService; import com.senmesh.sfile.service.UserFileService;
import com.senmesh.usercenter.user.dto.UserDTO;
import com.senmesh.usercenter.user.service.UserService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -33,6 +33,7 @@ import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.FileTime; import java.nio.file.attribute.FileTime;
import java.util.Comparator; import java.util.Comparator;
import java.util.Enumeration; import java.util.Enumeration;
@@ -63,6 +64,9 @@ public class TransferFileMonitorServiceImpl implements TransferFileMonitorServic
@Autowired @Autowired
private FileRiskService fileRiskService; private FileRiskService fileRiskService;
@Autowired
private UserService userService;
@Override @Override
public void scanAndProcess() { public void scanAndProcess() {
String transferReceiveDir = fileProperties.getTransfer().getReceiveDir(); String transferReceiveDir = fileProperties.getTransfer().getReceiveDir();
@@ -137,6 +141,10 @@ public class TransferFileMonitorServiceImpl implements TransferFileMonitorServic
return; return;
} }
if (!resolveTargetUser(zipFile, metadata)) {
return;
}
log.info("文件大小校验通过,开始解包: {}", fileName); log.info("文件大小校验通过,开始解包: {}", fileName);
// 2. 解包并创建用户文件(传入已解析的元数据,直接创建为正常状态) // 2. 解包并创建用户文件(传入已解析的元数据,直接创建为正常状态)
@@ -154,6 +162,66 @@ public class TransferFileMonitorServiceImpl implements TransferFileMonitorServic
} }
} }
/**
* 根据源端用户手机号和用户类型映射目标端用户。映射失败时改名为 .error,避免定时任务重复解包。
*/
private boolean resolveTargetUser(Path zipFile, TransferMetadataDTO metadata) {
String fileName = zipFile.getFileName().toString();
String sourceUserPhone = StringUtils.trim(metadata.getSourceUserPhone());
Integer sourceUserType = metadata.getSourceUserType();
if (StringUtils.isBlank(sourceUserPhone) || sourceUserType == null) {
renameToError(zipFile, "元数据缺少源端用户手机号或用户类型");
return false;
}
List<UserDTO> users = userService.listUncanceledByExactPhoneAndType(sourceUserPhone, sourceUserType);
if (users.size() != 1) {
renameToError(zipFile, "目标端用户匹配数量异常: phone=" + maskPhone(sourceUserPhone)
+ ", userType=" + sourceUserType + ", count=" + users.size());
return false;
}
Long targetUserId = users.get(0).getId();
ExpandUserFileDTO expandUserFile = metadata.getExpandUserFile();
if (expandUserFile == null || expandUserFile.getFolder() == null) {
renameToError(zipFile, "元数据缺少文件夹信息");
return false;
}
Long sourceUserId = metadata.getSourceUserId() == null
? expandUserFile.getFolder().getUserId()
: metadata.getSourceUserId();
expandUserFile.getFolder().setUserId(targetUserId);
if (expandUserFile.getFiles() != null) {
for (UserFileDTO file : expandUserFile.getFiles()) {
file.setUserId(targetUserId);
}
}
log.info("传输用户映射成功: file={}, sourceUserId={}, sourcePhone={}, sourceType={}, targetUserId={}",
fileName, sourceUserId, maskPhone(sourceUserPhone), sourceUserType, targetUserId);
return true;
}
private void renameToError(Path zipFile, String reason) {
String fileName = zipFile.getFileName().toString();
Path errorFile = zipFile.resolveSibling(fileName + ".error");
try {
Files.move(zipFile, errorFile, StandardCopyOption.REPLACE_EXISTING);
log.error("传输文件停止解包并改名为 .error: file={}, errorFile={}, reason={}",
fileName, errorFile.getFileName(), reason);
} catch (Exception e) {
log.error("传输文件停止解包但改名 .error 失败: file={}, reason={}", fileName, reason, e);
}
}
private String maskPhone(String phone) {
if (StringUtils.length(phone) < 7) {
return phone;
}
return phone.substring(0, 3) + "****" + phone.substring(phone.length() - 4);
}
/** /**
@@ -507,4 +575,3 @@ public class TransferFileMonitorServiceImpl implements TransferFileMonitorServic
} }
} }
} }

View File

@@ -1,5 +1,6 @@
package com.senmesh.sfile.service.impl; package com.senmesh.sfile.service.impl;
import com.senmesh.common.exception.util.Assert;
import com.senmesh.common.util.JsonUtils; import com.senmesh.common.util.JsonUtils;
import com.senmesh.common.util.UserContextHolder; import com.senmesh.common.util.UserContextHolder;
import com.senmesh.component.file.config.FileProperties; import com.senmesh.component.file.config.FileProperties;
@@ -14,6 +15,8 @@ import com.senmesh.sfile.dto.TransferUserFileCreateDTO;
import com.senmesh.sfile.dto.UserFileDTO; import com.senmesh.sfile.dto.UserFileDTO;
import com.senmesh.sfile.service.TransferUserFileService; import com.senmesh.sfile.service.TransferUserFileService;
import com.senmesh.sfile.service.UserFileService; import com.senmesh.sfile.service.UserFileService;
import com.senmesh.usercenter.user.dto.UserDTO;
import com.senmesh.usercenter.user.service.UserService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@@ -25,9 +28,7 @@ import java.io.InputStream;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream; import java.util.zip.ZipOutputStream;
@@ -47,6 +48,9 @@ public class TransferUserFileServiceImpl implements TransferUserFileService {
@Autowired @Autowired
private FileService fileService; private FileService fileService;
@Autowired
private UserService userService;
@Override @Override
@Transactional @Transactional
public Long create(TransferUserFileCreateDTO createDTO) { public Long create(TransferUserFileCreateDTO createDTO) {
@@ -92,6 +96,11 @@ public class TransferUserFileServiceImpl implements TransferUserFileService {
private void packTransferFiles(ExpandUserFileDTO expandUserFileDTO) throws IOException { private void packTransferFiles(ExpandUserFileDTO expandUserFileDTO) throws IOException {
UserFileDTO folder = expandUserFileDTO.getFolder(); UserFileDTO folder = expandUserFileDTO.getFolder();
List<UserFileDTO> files = expandUserFileDTO.getFiles(); List<UserFileDTO> files = expandUserFileDTO.getFiles();
UserDTO sourceUser = userService.get(folder.getUserId());
Assert.notNull(sourceUser, "源端用户不存在");
Assert.notBlank(sourceUser.getPhone(), "源端用户手机号不能为空");
Assert.notNull(sourceUser.getType(), "源端用户类型不能为空");
String sourceUserPhone = sourceUser.getPhone().trim();
// 1. 创建传输发送目录 // 1. 创建传输发送目录
String transferSendDir = fileProperties.getTransfer().getSendDir(); String transferSendDir = fileProperties.getTransfer().getSendDir();
@@ -133,6 +142,9 @@ public class TransferUserFileServiceImpl implements TransferUserFileService {
// 2.2 添加元数据文件 // 2.2 添加元数据文件
TransferMetadataDTO metadata = TransferMetadataDTO.builder() TransferMetadataDTO metadata = TransferMetadataDTO.builder()
.expandUserFile(expandUserFileDTO) .expandUserFile(expandUserFileDTO)
.sourceUserId(sourceUser.getId())
.sourceUserPhone(sourceUserPhone)
.sourceUserType(sourceUser.getType())
.build(); .build();
String metadataJson = JsonUtils.toJsonString(metadata); String metadataJson = JsonUtils.toJsonString(metadata);
@@ -145,4 +157,3 @@ public class TransferUserFileServiceImpl implements TransferUserFileService {
log.info("传输文件打包完成: {}", finalFilePath); log.info("传输文件打包完成: {}", finalFilePath);
} }
} }

View File

@@ -181,4 +181,13 @@ public interface UserMapper extends MyMapper<User> {
List<PlatformUserInfoDTO> listPlatformUser(@Param("param") PlatformUserListQueryDTO queryDTO); List<PlatformUserInfoDTO> listPlatformUser(@Param("param") PlatformUserListQueryDTO queryDTO);
List<UserDTO> listByRealNameOrPhone(@Param("orgId") Long orgId, @Param("realNames") Set<String> realNames, @Param("phones") Set<String> phones); List<UserDTO> listByRealNameOrPhone(@Param("orgId") Long orgId, @Param("realNames") Set<String> realNames, @Param("phones") Set<String> phones);
/**
* 根据手机号和用户类型精确查询未注销用户
*
* @param phone 手机号
* @param type 用户类型
* @return 用户列表
*/
List<UserDTO> listUncanceledByExactPhoneAndType(@Param("phone") String phone, @Param("type") Integer type);
} }

View File

@@ -91,6 +91,17 @@
) tab ) tab
</select> </select>
<select id="listUncanceledByExactPhoneAndType" resultType="com.senmesh.usercenter.user.dto.UserDTO">
select
uu.*
from uc_user uu
where uu.deleted = FALSE
and uu.status != 3
and uu.phone = #{phone}
and uu.type = #{type}
order by uu.id desc
</select>
<update id="initUserPassword"> <update id="initUserPassword">
update update
uc_user uc_user

View File

@@ -55,6 +55,15 @@ public interface UserService {
*/ */
List<UserDTO> list(UserQueryDTO userQueryDTO); List<UserDTO> list(UserQueryDTO userQueryDTO);
/**
* 根据手机号和用户类型精确查询未注销用户
*
* @param phone 手机号
* @param type 用户类型
* @return 用户列表
*/
List<UserDTO> listUncanceledByExactPhoneAndType(String phone, Integer type);
/** /**
* 查询单个 * 查询单个
* @param userQueryDTO * @param userQueryDTO

View File

@@ -512,6 +512,13 @@ public class UserServiceImpl implements UserService {
return userMapper.listUserDTO(userQueryDTO); return userMapper.listUserDTO(userQueryDTO);
} }
@Override
public List<UserDTO> listUncanceledByExactPhoneAndType(String phone, Integer type) {
Assert.notBlank(phone, "手机号不能为空");
Assert.notNull(type, "用户类型不能为空");
return userMapper.listUncanceledByExactPhoneAndType(phone.trim(), type);
}
@Override @Override
public UserDTO find(UserQueryDTO userQueryDTO) { public UserDTO find(UserQueryDTO userQueryDTO) {
List<UserDTO> userDTOs = list(userQueryDTO); List<UserDTO> userDTOs = list(userQueryDTO);