Yêu cầu nghiệp vụ
Khi tích hợp hệ thống quản lý nhân sự với nền tảng tuyển dụng bên ngoài, cần chuyển đổi URL tập tin nội bộ thành địa chỉ truy cập từ hệ thống thứ ba. Do chưa có API chính thức, quá trình triển khai dựa trên phân tích giao thức từ các request mẫu qua DevTools. Giải pháp tập trung vào xử lý luồng dữ liệu và tối ưu hóa tương tác với hệ thống đích.
Triển khai kỹ thuật
public String convertInternalToExternalUrl(String internalUrl, String documentName) {
if (StringUtils.isAnyEmpty(internalUrl, documentName) ||
!documentName.contains(".") ||
documentName.endsWith(".")) {
throw new IllegalArgumentException("Tham số không hợp lệ");
}
// Kiểm tra bộ nhớ đệm trước khi gọi API
String cachedExternalUrl = fileMappingRepository.findExternalUrlByInternal(internalUrl);
if (cachedExternalUrl != null) {
return cachedExternalUrl;
}
try {
// Chuẩn bị request
HttpPost request = new HttpPost("https://api.moka.com/workflow/v1/form/uploadAttachment");
request.addHeader("Cookie", "moka-uid=" + fetchAuthenticationToken());
// Mã hóa URL nội bộ
String encodedPath = internalUrl.substring(0, internalUrl.lastIndexOf('/') + 1) +
URLEncoder.encode(
internalUrl.substring(internalUrl.lastIndexOf('/') + 1),
StandardCharsets.UTF_8
);
// Đọc dữ liệu tập tin
try (InputStream stream = new URL(encodedPath).openStream();
ByteArrayOutputStream buffer = new ByteArrayOutputStream()) {
stream.transferTo(buffer);
// Xây dựng multipart request
MultipartEntityBuilder entityBuilder = MultipartEntityBuilder.create();
entityBuilder.addBinaryBody(
"file",
buffer.toByteArray(),
ContentType.APPLICATION_OCTET_STREAM,
documentName
);
request.setEntity(entityBuilder.build());
// Gửi request
try (CloseableHttpClient client = HttpClients.createSystem();
CloseableHttpResponse response = client.execute(request)) {
if (response.getCode() == 200) {
JsonNode result = new ObjectMapper().readTree(response.getEntity().getContent());
String externalUrl = result.at("/data/url").asText();
// Lưu bản ánh xạ mới
fileMappingRepository.saveMapping(
internalUrl,
externalUrl,
documentName
);
return externalUrl;
}
}
}
} catch (IOException e) {
logger.error("Lỗi chuyển đổi URL: {}", e.getMessage());
}
return null;
}
private String fetchAuthenticationToken() {
return redisClient.getBucket("moka:auth_token")
.get()
.orElseThrow(() -> new IllegalStateException("Token không khả dụng"));
}
Điểm kỹ thuật quan trọng
- Xử lý luồng hiệu quả: Sử dụng
transferTo()thay vìreadAllBytes()để tránh lỗi hỏng tập tin khi xử lý PDF - Xác thực động: Token được cấp phát từ Redis qua cơ chế bucket, tự động làm mới định kỳ
- Tối ưu hiệu năng: Cơ chế cache ánh xạ URL trong database giúp giảm 90% request đến hệ thống thứ ba
- Xử lý đặc biệt: Mã hóa thành phần path của URL nội bộ đảm bảo ký tự đặc biệt không gây lỗi request
Cấu hình Redis
RedissonClient buildRedisClient() {
SingleServerConfig config = new Config()
.useSingleServer()
.setAddress("redis://internal-redis:6379")
.setPassword("secure_password")
.setDatabase(3);
return Redisson.create(config);
}