Merge branch 'feature/SUPPORT-8458_file' into release/1.0.0

This commit is contained in:
Eduard Tihomirov 2024-09-26 10:50:37 +03:00
commit 371cffabbd
15 changed files with 363 additions and 75 deletions

View file

@ -244,6 +244,10 @@
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-web</artifactId>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-s3</artifactId>
</dependency>
</dependencies>
<build>
<finalName>${parent.artifactId}</finalName>

View file

@ -34,7 +34,8 @@ public class ReplyingKafkaConfig {
private String groupId;
@Value("${ervu.kafka.reply.timeout:30}")
private long replyTimeout;
@Value("${ervu.kafka.excerpt.reply.topic}")
private String excerptReplyTopic;
@Value("${ervu.kafka.security.protocol}")
private String securityProtocol;
@Value("${ervu.kafka.login.module:org.apache.kafka.common.security.scram.ScramLoginModule}")
@ -85,6 +86,24 @@ public class ReplyingKafkaConfig {
return factory;
}
@Bean()
@Qualifier("excerpt-container")
public ConcurrentMessageListenerContainer<String, String> excerptReplyContainer(
ConcurrentKafkaListenerContainerFactory<String, String> factory) {
ConcurrentMessageListenerContainer<String, String> container = factory.createContainer(
excerptReplyTopic);
container.getContainerProperties().setGroupId(groupId);
return container;
}
@Bean()
@Qualifier("excerpt-template")
public ReplyingKafkaTemplate<String, String, String> excerptReplyingKafkaTemplate(
@Qualifier("ervu") ProducerFactory<String, String> pf,
@Qualifier("excerpt-container") ConcurrentMessageListenerContainer<String, String> container) {
return initReplyingKafkaTemplate(pf, container);
}
@Bean
@Qualifier("org")
public ConcurrentMessageListenerContainer<String, String> replyContainer(

View file

@ -0,0 +1,83 @@
package ru.micord.ervu.kafka.controller;
import java.util.Arrays;
import java.util.Optional;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import ru.micord.ervu.kafka.model.Data;
import ru.micord.ervu.kafka.model.ErvuOrgResponse;
import ru.micord.ervu.kafka.model.ExcerptResponse;
import ru.micord.ervu.kafka.service.ReplyingKafkaService;
import ru.micord.ervu.s3.S3Service;
import ru.micord.ervu.security.webbpm.jwt.model.Token;
import ru.micord.ervu.security.webbpm.jwt.service.JwtTokenService;
/**
* @author Eduard Tihomirov
*/
@RestController
public class ErvuKafkaController {
@Autowired
@Qualifier("excerpt-service")
private ReplyingKafkaService replyingKafkaService;
@Autowired
private S3Service s3Service;
@Autowired
private JwtTokenService jwtTokenService;
@Value("${ervu.kafka.excerpt.reply.topic}")
private String requestReplyTopic;
@Value("${ervu.kafka.excerpt.request.topic}")
private String requestTopic;
@Autowired
private ObjectMapper objectMapper;
@RequestMapping(value = "/kafka/excerpt")
public ResponseEntity<InputStreamResource> getExcerptFile(HttpServletRequest request) {
try {
String authToken = getAuthToken(request);
Token token = jwtTokenService.getToken(authToken);
String[] split = token.getUserAccountId().split(":");
String prnOid = split[0];
String ervuId = split[1];
Data data = new Data();
data.setOrgId_ERVU(ervuId);
data.setPrnOid(prnOid);
String kafkaResponse = replyingKafkaService.sendMessageAndGetReply(requestTopic, requestReplyTopic,
objectMapper.writeValueAsString(data)
);
ExcerptResponse excerptResponse = objectMapper.readValue(kafkaResponse, ExcerptResponse.class);
return s3Service.getFile(excerptResponse.getFileUrl());
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
private String getAuthToken(HttpServletRequest request) {
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
if (cookie.getName().equals("auth_token")) {
return cookie.getValue();
}
}
}
return null;
}
}

View file

@ -0,0 +1,66 @@
package ru.micord.ervu.kafka.model;
import java.io.Serializable;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
/**
* @author Eduard Tihomirov
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class ExcerptResponse implements Serializable {
private static final long serialVersionUID = 1L;
private String excerptId;
private String orgId;
private String fileUrl;
@JsonFormat(pattern = "dd.MM.yyyy HH:mm")
private Date departureDateTime;
private String timeZone;
public String getExcerptId() {
return excerptId;
}
public void setExcerptId(String excerptId) {
this.excerptId = excerptId;
}
public String getOrgId() {
return orgId;
}
public void setOrgId(String orgId) {
this.orgId = orgId;
}
public Date getDepartureDateTime() {
return departureDateTime;
}
public void setDepartureDateTime(Date departureDateTime) {
this.departureDateTime = departureDateTime;
}
public String getTimeZone() {
return timeZone;
}
public void setTimeZone(String timeZone) {
this.timeZone = timeZone;
}
public String getFileUrl() {
return fileUrl;
}
public void setFileUrl(String fileUrl) {
this.fileUrl = fileUrl;
}
}

View file

@ -0,0 +1,23 @@
package ru.micord.ervu.kafka.service.impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.kafka.requestreply.ReplyingKafkaTemplate;
import org.springframework.stereotype.Service;
/**
* @author Eduard Tihomirov
*/
@Service
@Qualifier("excerpt-service")
public class ExcerptReplyingKafkaServiceImpl extends BaseReplyingKafkaServiceImpl {
@Autowired
@Qualifier("excerpt-template")
private ReplyingKafkaTemplate<String, String, String> excerptReplyingKafkaTemplate;
@Override
protected ReplyingKafkaTemplate<String, String, String> getReplyingKafkaTemplate() {
return excerptReplyingKafkaTemplate;
}
}

View file

@ -0,0 +1,45 @@
package ru.micord.ervu.s3;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.client.builder.AwsClientBuilder;
import com.amazonaws.regions.Region;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author Eduard Tihomirov
*/
@Configuration
public class S3Connection {
@Value("${s3.out.endpoint}")
private String endpointOut;
@Value("${s3.out.port:9000}")
private int portOut;
@Value("${s3.out.access_key}")
private String accessKeyOut;
@Value("${s3.out.secret_key}")
private String secretKeyOut;
@Bean("outClient")
public AmazonS3 getS3OutClient() {
return getS3Client(endpointOut, portOut, accessKeyOut, secretKeyOut);
}
private static AmazonS3 getS3Client(String endpoint, int port, String accessKey,
String secretKey) {
AWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey);
String s3Endpoint = endpoint + ":" + port;
String region = Region.getRegion(Regions.DEFAULT_REGION).toString();
return AmazonS3ClientBuilder.standard()
.withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(s3Endpoint, region))
.withCredentials(new AWSStaticCredentialsProvider(credentials))
.build();
}
}

View file

@ -0,0 +1,42 @@
package ru.micord.ervu.s3;
import java.io.File;
import java.net.URI;
import com.amazonaws.AmazonServiceException;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3URI;
import com.amazonaws.services.s3.model.S3Object;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
/**
* @author Eduard Tihomirov
*/
@Service
public class S3Service {
private final AmazonS3 outClient;
@Autowired
public S3Service(AmazonS3 outClient) {
this.outClient = outClient;
}
public ResponseEntity<InputStreamResource> getFile(String fileUrl) {
try {
AmazonS3URI uri = new AmazonS3URI(fileUrl);
S3Object s3Object = outClient.getObject(uri.getBucket(), uri.getKey());
InputStreamResource resource = new InputStreamResource(s3Object.getObjectContent());
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + uri.getKey())
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(resource);
}
catch (AmazonServiceException e) {
throw new RuntimeException(e);
}
}
}

View file

@ -433,8 +433,6 @@ public class EsiaAuthService {
catch (Exception e) {
throw new RuntimeException(e);
}
}
private OrgInfo copyToOrgInfo(OrganizationModel organizationModel, EmployeeModel employeeModel, EmployeeModel chiefModel ) {

View file

@ -40,3 +40,5 @@ ERVU_KAFKA_SECURITY_PROTOCOL=SASL_PLAINTEXT
ERVU_KAFKA_SASL_MECHANISM=SCRAM-SHA-256
ERVU_KAFKA_USERNAME=user1
ERVU_KAFKA_PASSWORD=Blfi9d2OFG
ERVU_KAFKA_EXCERPT_REPLY_TOPIC=ervu.lkrp.excerpt.response
ERVU_KAFKA_EXCERPT_REQUEST_TOPIC=ervu.lkrp.excerpt.request

View file

@ -89,6 +89,8 @@
<property name="esnsi.okopf.url" value="https://esnsi.gosuslugi.ru/rest/ext/v1/classifiers/11465/file?extension=JSON&amp;encoding=UTF_8"/>
<property name="ervu.kafka.journal.request.topic" value="ervu.organization.journal.request"/>
<property name="ervu.kafka.journal.reply.topic" value="ervu.organization.journal.response"/>
<property name="ervu.kafka.excerpt.reply.topic" value="ervu.lkrp.excerpt.response"/>
<property name="ervu.kafka.excerpt.request.topic" value="ervu.lkrp.excerpt.request"/>
</system-properties>
<management>
<audit-log>

View file

@ -0,0 +1,9 @@
<div>
<button type="button"
[disabled]="!isEnabled()"
class="btn btn-secondary"
[ngbTooltip]="tooltip | emptyIfNull"
(click)="onClick()"
(focus)="onFocus()"
(blur)="onBlur()">{{caption}}</button>
</div>

View file

@ -0,0 +1,42 @@
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef} from "@angular/core";
import {AbstractButton, NotNull} from "@webbpm/base-package";
import {HttpClient} from "@angular/common/http";
/**
* @author: Eduard Tihomirov
*/
@Component({
selector: 'ervu-download-file-button',
templateUrl: "./../../../../../src/resources/template/ervu/component/button/Button.html",
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ErvuDownloadFileButton extends AbstractButton {
private httpClient: HttpClient;
@NotNull()
public fileName: string;
constructor(el: ElementRef, cd: ChangeDetectorRef) {
super(el, cd);
}
initialize() {
super.initialize();
this.httpClient = this.injector.get(HttpClient);
}
public doClickActions(): Promise<any> {
return this.httpClient.get('kafka/excerpt', {
responseType: 'blob'
}).toPromise().then((response) => {
const url = window.URL.createObjectURL(response);
const a = document.createElement('a');
a.href = url;
a.download = this.fileName;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
a.remove();
});
}
}

View file

@ -24,6 +24,7 @@ import {AppProgressIndicationService} from "./service/app-progress-indication.se
import {FileUploadModule} from "ng2-file-upload";
import {ErvuFileUpload} from "../../ervu/component/fileupload/ErvuFileUpload";
import {InMemoryStaticGrid} from "../../ervu/component/grid/InMemoryStaticGrid";
import {ErvuDownloadFileButton} from "../../ervu/component/button/ErvuDownloadFileButton";
registerLocaleData(localeRu);
export const DIRECTIVES = [
@ -34,6 +35,7 @@ export const DIRECTIVES = [
forwardRef(() => AccessDeniedComponent),
forwardRef(() => AppProgressIndicationComponent),
forwardRef(() => ErvuFileUpload),
forwardRef(() => ErvuDownloadFileButton),
forwardRef(() => InMemoryStaticGrid)
];

View file

@ -367,6 +367,11 @@
<artifactId>log4j-web</artifactId>
<version>2.23.1</version>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-s3</artifactId>
<version>1.12.759</version>
</dependency>
</dependencies>
</dependencyManagement>
<repositories>

View file

@ -638,76 +638,6 @@
</entry>
</properties>
</scripts>
<scripts id="1996166f-7922-4f28-a571-9646d956ef37">
<properties>
<entry>
<key>enableCellTextSelection</key>
<value>
<simple>null</simple>
</value>
</entry>
<entry>
<key>fetchSize</key>
<value>
<simple>20.0</simple>
</value>
</entry>
<entry>
<key>headerHeight</key>
<value>
<simple>40.0</simple>
</value>
</entry>
<entry>
<key>loadingOverlayMessage</key>
<value>
<simple>"Загрузка данных, пожалуйста, подождите."</simple>
</value>
</entry>
<entry>
<key>loadingOverlayType</key>
<value>
<simple>"PROGRESS_BAR"</simple>
</value>
</entry>
<entry>
<key>noRowsOverlayMessage</key>
<value>
<simple>"Данные отсутствуют"</simple>
</value>
</entry>
<entry>
<key>rowClickSelectionType</key>
<value>
<simple>"SINGLE_SELECT_CLICK"</simple>
</value>
</entry>
<entry>
<key>rowHeight</key>
<value>
<simple>40.0</simple>
</value>
</entry>
<entry>
<key>rowModelType</key>
<value>
<simple>"CLIENT_SIDE"</simple>
</value>
</entry>
<entry>
<key>showRowNumber</key>
<value>
<simple>null</simple>
</value>
</entry>
<entry>
<key>theme</key>
<value>
<simple>null</simple>
</value>
</entry>
</properties>
</scripts>
<scripts id="be8fe0e1-4909-4224-8664-be55168595c6"/>
<scripts id="f0a59cd9-acf8-4b1d-89fa-a5d618fc4664">
<classRef type="JAVA">
@ -1347,6 +1277,16 @@
<container>false</container>
<childrenReordered>false</childrenReordered>
<scripts id="bf098f19-480e-44e4-9084-aa42955c4d0f">
<removed>true</removed>
<expanded>false</expanded>
</scripts>
<scripts id="38036714-7fff-4404-98d3-b0f5cc846368">
<classRef type="TS">
<className>ErvuDownloadFileButton</className>
<packageName>ervu.component.button</packageName>
</classRef>
<enabled>true</enabled>
<expanded>true</expanded>
<properties>
<entry>
<key>caption</key>
@ -1355,9 +1295,15 @@
</value>
</entry>
<entry>
<key>tooltip</key>
<key>fileName</key>
<value>
<simple>null</simple>
<simple>"Выписка"</simple>
</value>
</entry>
<entry>
<key>visible</key>
<value>
<simple>true</simple>
</value>
</entry>
</properties>