This commit is contained in:
nguyennt1 2025-11-14 23:23:11 +07:00
parent 19225eca0d
commit 86a4e6fc76
37 changed files with 1562 additions and 0 deletions

6
.gitignore vendored
View File

@ -24,3 +24,9 @@
hs_err_pid*
replay_pid*
# Ignore Gradle project-specific cache directory
.gradle
# Ignore Gradle build output directory
build

54
config/log4j2.properties Normal file
View File

@ -0,0 +1,54 @@
property.log.dir=logs/${spring:spring.application.name}
property.log.dir.info=${log.dir}/info
property.log.dir.error=${log.dir}/error
property.log.pattern=%d{yyyy-MM-dd HH:mm:ss} - [%t] - %p - %m%n
# Console Logger
appender.Console.type=Console
appender.Console.name=Console
appender.Console.target=SYSTEM_OUT
appender.Console.layout.type=PatternLayout
appender.Console.layout.pattern=${log.pattern}
# Info Log File - Rolling by Day
appender.InfoFile.type=RollingFile
appender.InfoFile.name=InfoFile
appender.InfoFile.fileName=${log.dir.info}/app-info.log
appender.InfoFile.filePattern=${log.dir.info}/app-info-%d{yyyy-MM-dd}.log.gz
appender.InfoFile.layout.type=PatternLayout
appender.InfoFile.layout.pattern=${log.pattern}
appender.InfoFile.filter.0.type=ThresholdFilter
appender.InfoFile.filter.0.level=INFO
appender.InfoFile.filter.0.onMatch=ACCEPT
appender.InfoFile.filter.0.onMismatch=NEUTRAL
appender.InfoFile.filter.1.type=ThresholdFilter
appender.InfoFile.filter.1.level=WARN
appender.InfoFile.filter.1.onMatch=ACCEPT
appender.InfoFile.filter.1.onMismatch=DENY
appender.InfoFile.policies.type=Policies
appender.InfoFile.policies.timeBased.type=TimeBasedTriggeringPolicy
appender.InfoFile.policies.timeBased.interval=1
appender.InfoFile.policies.timeBased.modulate=true
# Error Log File - Rolling by Day
appender.ErrorFile.type=RollingFile
appender.ErrorFile.name=ErrorFile
appender.ErrorFile.fileName=${log.dir.error}/app-error.log
appender.ErrorFile.filePattern=${log.dir.error}/app-error-%d{yyyy-MM-dd}.log.gz
appender.ErrorFile.layout.type=PatternLayout
appender.ErrorFile.layout.pattern=${log.pattern}
appender.ErrorFile.filter.threshold.type=ThresholdFilter
appender.ErrorFile.filter.threshold.level=ERROR
appender.ErrorFile.policies.type=Policies
appender.ErrorFile.policies.timeBased.type=TimeBasedTriggeringPolicy
appender.ErrorFile.policies.timeBased.interval=1
appender.ErrorFile.policies.timeBased.modulate=true
# Root Logger
rootLogger.level=INFO
rootLogger.appenderRef.Console.ref=Console
rootLogger.appenderRef.InfoFile.ref=InfoFile
rootLogger.appenderRef.ErrorFile.ref=ErrorFile

35
config/shared.properties Normal file
View File

@ -0,0 +1,35 @@
spring.jpa.open-in-view=false
app.datasource.vega.url=jdbc:postgresql://103.166.141.147:5440/db_vega_hrm
app.datasource.vega.username=vega_hrm
app.datasource.vega.password=VegaHrm@2025
app.datasource.vega.hikari.idle-timeout=10000
app.datasource.vega.hikari.maximum-pool-size=10
app.datasource.vega.hikari.minimum-idle=5
app.datasource.vega.hikari.pool-name=VEGA_POOL
app.datasource.vega.hibernate.default_schema=vega_hrm
# Hibernate PostgreSQL dialect
app.datasource.vega.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
app.datasource.vega.hibernate.format_sql=true
app.datasource.vega.hikari.show-sql=true
app.datasource.vega.hikari.max-lifetime=1800000
app.datasource.vega.hikari.connection-timeout=30000
app.datasource.vega.hikari.validation-timeout=5000
vega.hrm.postgre.enabled=true
#app.datasource.core.vcb.url=jdbc:oracle:thin:@//10.22.19.197:1521/DVNHTEST
#app.datasource.core.vcb.username=VCB_AIRPORT_LOUNGE
#app.datasource.core.vcb.password=TGwE247q0Hh7aZAAw6YDT1IP8f+z+SQINE4Gi8GgE48=
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql=TRACE
#spring.data.redis.sentinel.master=redismaster
#spring.data.redis.sentinel.nodes=10.22.18.138:26379,10.22.18.139:26379,10.22.18.140:26379
#spring.data.redis.sentinel.password=dLF5ZnOeOHM6/pzTHezV+WNXU+F7ZBGu85f8bwzk
#spring.data.redis.sentinel.database=17
cors.allowed-origins=http://localhost:4201,http://localhost:4202,http://localhost:4200,http://localhost:4203
#vcb-lounge.redis.time-to-live=600
#spring.cache.type=simple

View File

@ -0,0 +1,162 @@
package com.vega.hrm.configs;
import com.zaxxer.hikari.HikariDataSource;
import java.sql.Connection;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import javax.sql.DataSource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@Configuration
@Slf4j
@Order(2)
@EnableJpaRepositories(
basePackages = "${vega.jpa.repository.basePackage}",
entityManagerFactoryRef = "vegaHrmEntityManager",
transactionManagerRef = "vegaHrmTransactionManager"
)
@ConditionalOnProperty(name = "vega.hrm.postgre.enabled", havingValue = "true")
@EnableTransactionManagement
public class VegaDatabaseConfig {
@Value("${vega.jpa.entity.basePackage:com.core.entity}")
private String entityBasePackage;
@Value("${app.datasource.vega.hikari.maximum-pool-size:10}")
private Integer hikariMaximumPoolSize;
@Value("${app.datasource.vega.hikari.minimum-idle:5}")
private Integer hikariMinimumIdle;
@Value("${app.datasource.vega.hikari.connection-timeout:30000}")
private Long hikariConnectionTimeout;
@Value("${app.datasource.vega.hikari.idle-timeout:10000}")
private Long hikariIdleTimeout;
@Value("${app.datasource.vega.hikari.max-lifetime:1800000}")
private Long hikariMaxLifeTime;
@Value("${app.datasource.vega.hikari.pool-name:VCB_LOUNGE_POOL}")
private String hikariPoolName;
@Value("${app.datasource.vega.hikari.validation-timeout:5000}")
private Long validationTimeout;
@Value("${app.datasource.vega.hibernate.default_schema:}")
private String hibernateDefaultSchema;
@Bean
@ConfigurationProperties(prefix = "app.datasource.vega")
public DataSourceProperties vegaHrmDataSourceProperties() {
return new DataSourceProperties();
}
@Bean(name = "vegaDataSource")
public DataSource vegaDataSource() {
var dataSourceProperties = vegaHrmDataSourceProperties();
return configureHikariDataSource(dataSourceProperties);
}
private DataSource configureHikariDataSource(DataSourceProperties properties) {
var dataSource = properties
.initializeDataSourceBuilder()
.type(HikariDataSource.class)
.build();
// Hikari settings
if (hikariMaximumPoolSize != null) {
dataSource.setMaximumPoolSize(hikariMaximumPoolSize);
}
if (hikariMinimumIdle != null) {
dataSource.setMinimumIdle(hikariMinimumIdle);
}
if (hikariConnectionTimeout != null) {
dataSource.setConnectionTimeout(hikariConnectionTimeout);
}
if (hikariMaxLifeTime != null) {
dataSource.setMaxLifetime(hikariMaxLifeTime);
}
if (hikariIdleTimeout != null) {
dataSource.setIdleTimeout(hikariIdleTimeout);
}
if (hikariPoolName != null) {
dataSource.setPoolName(hikariPoolName);
}
if (validationTimeout != null) {
dataSource.setValidationTimeout(validationTimeout);
}
// PostgreSQL driver
dataSource.setDriverClassName("org.postgresql.Driver");
log.info(
"HikariCP Pool Config: MaxPoolSize={}, MinIdle={}, IdleTimeout={}, MaxLifetime={}, ConnectionTimeout={}",
hikariMaximumPoolSize, hikariMinimumIdle, hikariIdleTimeout, hikariMaxLifeTime,
hikariConnectionTimeout);
return dataSource;
}
@Bean
public LocalContainerEntityManagerFactoryBean vegaHrmEntityManager(
@Qualifier("vegaDataSource") DataSource dataSource) {
var em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource);
em.setPackagesToScan(entityBasePackage);
em.setPersistenceUnitName("db1");
var vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
Map<String, Object> jpaProps = new HashMap<>();
jpaProps.put("hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect");
jpaProps.put("hibernate.format_sql", true);
jpaProps.put("hibernate.show_sql", false);
if (hibernateDefaultSchema != null && !hibernateDefaultSchema.isBlank()) {
jpaProps.put("hibernate.default_schema", hibernateDefaultSchema.trim());
}
em.setJpaPropertyMap(jpaProps);
loggingDatabaseSource(em);
return em;
}
private void loggingDatabaseSource(LocalContainerEntityManagerFactoryBean em) {
log.info("vcbDatabaseSourceConfig entityBasePackage : {}", entityBasePackage);
try (Connection connection = Objects.requireNonNull(em.getDataSource()).getConnection()) {
log.info("Database URL: {}", connection.getMetaData().getURL());
log.info("Database Product: {} {}", connection.getMetaData().getDatabaseProductName(),
connection.getMetaData().getDatabaseProductVersion());
} catch (Exception e) {
log.error("Error retrieving database URL", e);
}
}
@Bean
public PlatformTransactionManager vegaHrmTransactionManager(
@Qualifier("vegaDataSource") DataSource dataSource) {
var transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(vegaHrmEntityManager(dataSource).getObject());
return transactionManager;
}
}

View File

@ -0,0 +1,6 @@
package com.vega.hrm.constants;
public class KeySecurityConst {
public static String KEY_SECURITY_DATABASE = "lDCfYiZ5rxH1G1ElIth9ppqED6MgaLKZ";
public static final String JWT_KEY = "e2)q2Nzc%MQz_i(EeUZMla^sW8LpSNRV";
}

View File

@ -0,0 +1,8 @@
package com.vega.hrm.constants;
public class ResponseCodeConst {
public static final String SUCCESS = "00";
public static final String INVALID = "01";
public static final String NOT_FOUND = "02";
public static final String INTERNAL_SYSTEM_ERROR = "99";
}

View File

@ -0,0 +1,5 @@
package com.vega.hrm.constants;
public class ResponseMessageConst {
public static final String INTERNAL_SYSTEM_ERROR = "Lỗi hệ thống";
}

View File

@ -0,0 +1,37 @@
package com.vega.hrm.constants;
import java.util.HashMap;
public class UserHistoryConstant {
public static final String ACTION_TYPE_CREATE = "CREATE";
public static final String ACTION_TYPE_SEARCH = "SEARCH";
public static final String ACTION_TYPE_VIEW = "VIEW";
public static final String ACTION_TYPE_UPDATE = "UPDATE";
public static final String ACTION_TYPE_DELETE = "DELETE";
public static final String ACTION_TYPE_LOGIN = "LOGIN";
public static final String ACTION_TYPE_LOGOUT = "LOGOUT";
public static final String ACTION_TYPE_CHANGE_STATUS = "CHANGE_STATUS";
public static final String ACTION_TYPE_CHANGE_STATUS_MOBILE = "CHANGE_STATUS_MOBILE";
public static final String ACTION_TYPE_APPROVE = "APPROVE";
public static final String ACTION_TYPE_EXPORT = "EXPORT";
public static final String ACTION_TYPE_DOWNLOAD = "DOWNLOAD";
public static final String ACTION_TYPE_RESET_PASSWORD = "RESET_PASSWORD";
public static final String ACTION_DESC_TEMPLATE = "IP: %s - %s bởi %s";
public static final HashMap<String, String> ACTION_TYPE_NAMES = new HashMap<>() {
{
put(ACTION_TYPE_CREATE, "Thêm mới");
put(ACTION_TYPE_SEARCH, "Truy vấn");
put(ACTION_TYPE_VIEW, "Xem chi tiết");
put(ACTION_TYPE_UPDATE, "Cập nhật");
put(ACTION_TYPE_DELETE, "Xóa");
put(ACTION_TYPE_LOGIN, "Đăng nhập");
put(ACTION_TYPE_LOGOUT, "Đăng xuất");
put(ACTION_TYPE_CHANGE_STATUS, "Đổi trạng thái");
put(ACTION_TYPE_CHANGE_STATUS_MOBILE, "Đổi trạng thái SĐT");
put(ACTION_TYPE_APPROVE, "Phê duyệt");
put(ACTION_TYPE_EXPORT, "Kết xuất");
put(ACTION_TYPE_DOWNLOAD, "Tải file");
put(ACTION_TYPE_RESET_PASSWORD,"Reset password");
}
};
}

View File

@ -0,0 +1,62 @@
package com.vega.hrm.entity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import java.time.Instant;
import java.util.UUID;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.ColumnDefault;
@Getter
@Setter
@Entity
@Table(name = "bo_function")
public class BoFunction {
@Id
@ColumnDefault("gen_random_uuid()")
@Column(name = "function_id", nullable = false)
private UUID id;
@Size(max = 200)
@NotNull
@Column(name = "function_name", nullable = false, length = 200)
private String functionName;
@NotNull
@Column(name = "function_level", nullable = false)
private Long functionLevel;
@Size(max = 100)
@Column(name = "function_url", length = 100)
private String functionUrl;
@NotNull
@Column(name = "function_order", nullable = false)
private Long functionOrder;
@NotNull
@Column(name = "parent_id", nullable = false)
private Long parentId;
@Size(max = 20)
@NotNull
@Column(name = "created_by", nullable = false, length = 20)
private String createdBy;
@NotNull
@ColumnDefault("now()")
@Column(name = "create_time", nullable = false)
private Instant createTime;
@Size(max = 20)
@NotNull
@Column(name = "function_display", nullable = false, length = 20)
private String functionDisplay;
}

View File

@ -0,0 +1,35 @@
package com.vega.hrm.entity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import jakarta.validation.constraints.Size;
import java.util.UUID;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.ColumnDefault;
@Getter
@Setter
@Entity
@Table(name = "bo_new_func_auth")
public class BoNewFuncAuth {
@Id
@ColumnDefault("gen_random_uuid()")
@Column(name = "id", nullable = false)
private UUID id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "func_id")
private BoFunction func;
@Size(max = 255)
@Column(name = "api_endpoint")
private String apiEndpoint;
}

View File

@ -0,0 +1,49 @@
package com.vega.hrm.entity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import java.time.Instant;
import java.util.UUID;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.ColumnDefault;
@Getter
@Setter
@Entity
@Table(name = "bo_role")
public class BoRole {
@Id
@ColumnDefault("gen_random_uuid()")
@Column(name = "role_id", nullable = false)
private UUID id;
@Size(max = 200)
@NotNull
@Column(name = "role_name", nullable = false, length = 200)
private String roleName;
@Size(max = 100)
@NotNull
@Column(name = "created_by", nullable = false, length = 100)
private String createdBy;
@NotNull
@Column(name = "create_time", nullable = false)
private Instant createTime;
@Size(max = 1000)
@Column(name = "description", length = 1000)
private String description;
@Size(max = 1)
@NotNull
@Column(name = "status", nullable = false, length = 1)
private String status;
}

View File

@ -0,0 +1,48 @@
package com.vega.hrm.entity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import java.time.Instant;
import java.util.UUID;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.ColumnDefault;
@Getter
@Setter
@Entity
@Table(name = "bo_role_func")
public class BoRoleFunc {
@Id
@ColumnDefault("gen_random_uuid()")
@Column(name = "id", nullable = false)
private UUID id;
@NotNull
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "function_id", nullable = false)
private BoFunction function;
@NotNull
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "role_id", nullable = false)
private BoRole role;
@NotNull
@Column(name = "create_time", nullable = false)
private Instant createTime;
@Size(max = 30)
@NotNull
@Column(name = "create_user", nullable = false, length = 30)
private String createUser;
}

View File

@ -0,0 +1,89 @@
package com.vega.hrm.entity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import java.time.Instant;
import java.util.UUID;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.ColumnDefault;
@Getter
@Setter
@Entity
@Table(name = "bo_user")
public class BoUser {
@Id
@ColumnDefault("gen_random_uuid()")
@Column(name = "user_id", nullable = false)
private UUID id;
@Size(max = 200)
@NotNull
@Column(name = "user_name", nullable = false, length = 200)
private String userName;
@Size(max = 200)
@NotNull
@Column(name = "full_name", nullable = false, length = 200)
private String fullName;
@Size(max = 50)
@NotNull
@Column(name = "status", nullable = false, length = 50)
private String status;
@Size(max = 200)
@NotNull
@Column(name = "password", nullable = false, length = 200)
private String password;
@Column(name = "pwd_expire_date")
private Instant pwdExpireDate;
@NotNull
@Column(name = "created_date", nullable = false)
private Instant createdDate;
@Size(max = 200)
@NotNull
@Column(name = "created_user", nullable = false, length = 200)
private String createdUser;
@Column(name = "role_id")
private Long roleId;
@NotNull
@ColumnDefault("0")
@Column(name = "is_password_changed", nullable = false)
private Long isPasswordChanged;
@NotNull
@ColumnDefault("0")
@Column(name = "number_of_failed_logins", nullable = false)
private Long numberOfFailedLogins;
@Column(name = "last_login_time")
private Instant lastLoginTime;
@Column(name = "updated_date")
private Instant updatedDate;
@Size(max = 20)
@Column(name = "updated_user", length = 20)
private String updatedUser;
@Size(max = 100)
@Column(name = "uuid", length = 100)
private String uuid;
@Size(max = 255)
@Column(name = "email")
private String email;
}

View File

@ -0,0 +1,55 @@
package com.vega.hrm.exceptions;
import com.vega.hrm.constants.ResponseCodeConst;
import com.vega.hrm.constants.ResponseMessageConst;
import com.vega.hrm.models.responses.BaseResponse;
import java.util.HashMap;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<?> handleValidationExceptions(MethodArgumentNotValidException ex) {
var messageValid = new StringBuilder();
Map<String, String> errors = new HashMap<>();
ex.getBindingResult()
.getFieldErrors()
.forEach(
error -> {
messageValid.append(error.getField()).append(":").append(error.getDefaultMessage()).append(";");
errors.put(error.getField(), error.getDefaultMessage());
});
return ResponseEntity.ok(
BaseResponse.<Map<String, String>>builder()
.code(ResponseCodeConst.INVALID)
.message(messageValid.substring(0, messageValid.length() - 1))
.data(errors)
.build()
);
}
@ExceptionHandler({Exception.class})
public ResponseEntity<?> handleExceptions(Exception ex) {
log.error(ex.getMessage(), ex);
return new ResponseEntity<>(
BaseResponse.<Map<String, String>>builder()
.code(ResponseCodeConst.INTERNAL_SYSTEM_ERROR)
.message(ResponseMessageConst.INTERNAL_SYSTEM_ERROR)
.build(),
HttpStatus.INTERNAL_SERVER_ERROR
);
}
}

View File

@ -0,0 +1,30 @@
package com.vega.hrm.filters;
import io.jsonwebtoken.ExpiredJwtException;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import org.apache.logging.log4j.ThreadContext;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
@Component
@Order(2)
@RequiredArgsConstructor
public class AuthorizationFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
filterChain.doFilter(request, response);
}
}

View File

@ -0,0 +1,68 @@
package com.vega.hrm.filters;
import com.vega.hrm.helpers.LogHelper;
import jakarta.servlet.FilterChain;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.time.Instant;
import java.util.Arrays;
import java.util.UUID;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.ThreadContext;
import org.apache.logging.log4j.util.Strings;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
@Component
@Slf4j
@Order(1)
public class CorsFilter extends OncePerRequestFilter {
@Value("${cors.allowed-origins:*}")
private String[] allowedOrigins;
@Override
protected void doFilterInternal(
@NonNull HttpServletRequest request,
@NonNull HttpServletResponse response,
@NonNull FilterChain filterChain
) {
try {
var startTime = Instant.now();
var origin = request.getHeader("Origin");
if (Strings.isNotBlank(origin) && Arrays.stream(allowedOrigins).toList()
.contains(origin)) {
response.setHeader("Access-Control-Allow-Origin", origin);
}
response.setHeader("Access-Control-Allow-Methods", "POST, OPTIONS");
response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
response.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, b");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setCharacterEncoding("UTF-8");
if (request.getMethod().equalsIgnoreCase("OPTIONS")) {
response.setStatus(HttpServletResponse.SC_OK);
return;
}
var clientIp = request.getHeader("X-Forwarded-For");
if (Strings.isBlank(clientIp)) {
clientIp = request.getRemoteAddr();
} else {
clientIp = clientIp.split(",")[0];
}
ThreadContext.put("ip", clientIp);
ThreadContext.put("uri", request.getRequestURI());
var traceId = UUID.randomUUID().toString();
ThreadContext.put("traceId", traceId);
LogHelper.info("Request start");
filterChain.doFilter(request, response);
var duration = Instant.now().toEpochMilli() - startTime.toEpochMilli();
LogHelper.info("Request end - Duration: " + duration + "ms");
} catch (Exception e) {
LogHelper.error(e);
}
}
}

View File

@ -0,0 +1,117 @@
package com.vega.hrm.helpers;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.util.List;
import java.util.stream.Collectors;
import org.modelmapper.ModelMapper;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
public class CommonHelper {
private static ModelMapper modelMapper = new ModelMapper();
public static <S, T> List<T> mapList(List<S> source, Class<T> targetClass) {
return source
.stream()
.map(element -> modelMapper.map(element, targetClass))
.collect(Collectors.toList());
}
public static Long convertStringToLong(String str) {
if (str == null || str.trim().isEmpty()) {
return null;
}
try {
return Long.parseLong(str.trim());
} catch (NumberFormatException e) {
return null;
}
}
public static String getClientIp(HttpServletRequest request) {
if (request == null) {
return "Unknown";
}
String ip = request.getHeader("X-Forwarded-For"); // Header used by proxies/load balancers
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr(); // Direct IP
}
if (ip != null && ip.contains(",")) {
ip = ip.split(",")[0].trim();
}
return ip;
}
public static String getClientIp() {
HttpServletRequest request = getCurrentHttpRequest();
if (request == null) {
return "Unknown";
}
return getClientIp(request);
}
private static HttpServletRequest getCurrentHttpRequest() {
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
if (requestAttributes == null) {
return null;
}
return ((ServletRequestAttributes) requestAttributes).getRequest();
}
public static <T> String convertObjectToCommaSeparated(T object){
if (object == null) {
return "";
}
if (object instanceof List<?> list) {
return list.stream()
.map(CommonHelper::convertObjectToCommaSeparated)
.collect(Collectors.joining("\n"));
}
StringBuilder result = new StringBuilder();
Field[] fields = object.getClass().getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true); // Cho phép truy cập vào các trường private
try {
Object value = field.get(object);
result.append(value).append(",");
} catch (IllegalAccessException e) {
LogHelper.error(e.getMessage());
}
}
if (!result.isEmpty()) {
result.deleteCharAt(result.length() - 1);
}
return result.toString();
}
public static String convertObjectToString(Object object) {
try{
ObjectMapper mapper = new ObjectMapper();
return mapper.writeValueAsString(object);
}
catch (Exception e) {
LogHelper.error(e.getMessage());
return "";
}
}
}

View File

@ -0,0 +1,36 @@
package com.vega.hrm.helpers;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class CryptoHelper {
public static String hashSHA256(String input) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(input.getBytes(StandardCharsets.UTF_8));
return bytesToHex(hash);
} catch (NoSuchAlgorithmException ex) {
log.error(ex.getMessage());
return "";
}
}
private static String bytesToHex(byte[] hash) {
StringBuilder hexString = new StringBuilder(2 * hash.length);
for (int i = 0; i < hash.length; i++) {
String hex = Integer.toHexString(0xff & hash[i]);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
}
}

View File

@ -0,0 +1,111 @@
package com.vega.hrm.helpers;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class DateHelper {
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("dd/MM/yyyy");
public static Date convertStringToDate(String dateString) {
SimpleDateFormat formatter = new SimpleDateFormat("dd/MM/yyyy");
formatter.setLenient(false);
try {
return formatter.parse(dateString);
} catch (ParseException e) {
log.error("convertStringToDate Lỗi format dd/MM/yyyy");
return null;
}
}
public static Instant convertStringToInstant(String dateString, String pattern) {
try {
if (dateString == null || dateString.isEmpty()) {
pattern = "dd/MM/yyyy";
}
if (dateString == null || dateString.isEmpty()) {
return null;
}
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
LocalDateTime localDateTime;
if (pattern.equals("dd/MM/yyyy")) {
LocalDate localDate = LocalDate.parse(dateString, formatter);
LocalTime now = LocalTime.now();
localDateTime = LocalDateTime.of(localDate, now);
} else {
localDateTime = LocalDateTime.parse(dateString, formatter);
}
return localDateTime.toInstant(ZoneOffset.UTC);
} catch (Exception e) {
return null;
}
}
public static String convertDateToString(Date date, String format) {
if (date == null) {
return null;
}
SimpleDateFormat formatter = new SimpleDateFormat(format);
return formatter.format(date);
}
public static String convertLocalDateToString(LocalDateTime date, String format) {
if (date == null) {
return null;
}
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);
return date.format(formatter);
}
public static Date getStartOfDay(String dateStr) {
LocalDate localDate = LocalDate.parse(dateStr, FORMATTER);
LocalDateTime startOfDay = localDate.atStartOfDay().plusSeconds(1); // 00:00:01
return Date.from(startOfDay.atZone(ZoneId.of("UTC")).toInstant());
}
public static Date convertDateZone(String dateStr,String pattern) {
DateTimeFormatter format = DateTimeFormatter.ofPattern(pattern);
LocalDate localDate = LocalDate.parse(dateStr, format);
LocalDateTime startOfDay = localDate.atStartOfDay().plusSeconds(1); // 00:00:01
return Date.from(startOfDay.atZone(ZoneId.of("UTC")).toInstant());
}
public static LocalDateTime getStartOfDayLocalDate(String dateStr) {
LocalDate localDate = LocalDate.parse(dateStr, FORMATTER);
LocalDateTime startOfDay = localDate.atStartOfDay().plusSeconds(1); // 00:00:01
return startOfDay.atZone(ZoneId.of("UTC")).toLocalDateTime();
}
public static LocalDateTime convertDateZoneLocalDate(String dateStr,String pattern) {
DateTimeFormatter format = DateTimeFormatter.ofPattern(pattern);
LocalDate localDate = LocalDate.parse(dateStr, format);
LocalDateTime startOfDay = localDate.atStartOfDay().plusSeconds(1); // 00:00:01
return LocalDateTime.from(startOfDay.atZone(ZoneId.of("UTC")).toInstant());
}
public static LocalDateTime getEndOfDayLocalDate(String dateStr) {
LocalDate localDate = LocalDate.parse(dateStr, FORMATTER);
LocalDateTime endOfDay = localDate.atTime(23, 59, 59); // 00:00:01
return endOfDay.atZone(ZoneId.of("UTC")).toLocalDateTime();
}
public static Date getEndOfDay(String dateStr) {
LocalDate localDate = LocalDate.parse(dateStr, FORMATTER);
LocalDateTime endOfDay = localDate.atTime(23, 59, 59); // 23:59:59
return Date.from(endOfDay.atZone(ZoneId.of("UTC")).toInstant());
}
}

View File

@ -0,0 +1,38 @@
package com.vega.hrm.helpers;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class JsonConvertHelper {
private static final ObjectMapper objectMapper = new ObjectMapper();
public static String toJsonString(Object object) {
try {
JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addSerializer(LocalDateTime.class,
new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm:ss")));
objectMapper.registerModule(javaTimeModule);
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
objectMapper.findAndRegisterModules();
return objectMapper.writeValueAsString(object);
} catch (JsonProcessingException e) {
LogHelper.error(e);
return "";
}
}
public static <T> T convertStringToObject(String text,Class<T> clazz) {
try {
return objectMapper.readValue(text,clazz);
} catch (JsonProcessingException e) {
LogHelper.error(e);
return null;
}
}
}

View File

@ -0,0 +1,79 @@
package com.vega.hrm.helpers;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.UnsupportedJwtException;
import io.jsonwebtoken.security.Keys;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.util.Date;
import java.util.Map;
import javax.crypto.spec.SecretKeySpec;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class JwtHelper {
private static final String SECRET_KEY = "b05f17acb2d09f123a472406051512ca08b";
private static final long EXPIRATION_TIME_MS = 600_000; // 10 phut
private static final Key SIGNING_KEY = generateSigningKey();
public static String generateToken(String username, Long userId, Long roleId) {
return Jwts.builder()
.claims(Map.of(
"userId", userId,
"roleId", roleId,
"userName", username
))
.subject(username)
.issuedAt(new Date())
.expiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME_MS))
.signWith(SIGNING_KEY)
.compact();
}
private static Key generateSigningKey() {
return Keys.hmacShaKeyFor(SECRET_KEY.getBytes(StandardCharsets.UTF_8));
}
public static Long getUserId(String token) {
Claims claims = extractUsername(token);
return Long.parseLong(claims.get("userId").toString());
}
public static Long getRoleId(String token) {
Claims claims = extractUsername(token);
return Long.parseLong(claims.get("roleId").toString());
}
public static String getDataByKey(String token, String key) {
Claims claims = extractUsername(token);
return claims.get(key).toString();
}
public static Claims extractUsername(String token) {
return Jwts.parser()
.verifyWith(new SecretKeySpec(SECRET_KEY.getBytes(StandardCharsets.UTF_8), "HmacSHA256"))
.build()
.parseSignedClaims(token)
.getPayload();
}
public static boolean validateToken(String authToken) {
try {
extractUsername(authToken);
return true;
} catch (MalformedJwtException ex) {
log.error("Invalid JWT token");
} catch (ExpiredJwtException ex) {
log.error("Expired JWT token");
} catch (UnsupportedJwtException ex) {
log.error("Unsupported JWT token");
} catch (IllegalArgumentException ex) {
log.error("JWT claims string is empty.");
}
return false;
}
}

View File

@ -0,0 +1,45 @@
package com.vega.hrm.helpers;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.ThreadContext;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class LogHelper {
public static void info(String message) {
log.info("{}", getLogContent(message));
}
public static void error(String message) {
log.error("{}", getLogContent(message));
}
public static void error(Exception e) {
var traceBuilder = new StringBuilder();
for (var stackTraceElement : e.getStackTrace()) {
traceBuilder.append(String.format(
"\n at %s.%s (%s:%s)",
stackTraceElement.getClassName(),
stackTraceElement.getMethodName(),
stackTraceElement.getFileName(),
stackTraceElement.getLineNumber()
));
}
log.error("{}", getLogContent(String.format("%s: %s%s", e.getClass().getName(), e.getMessage(), traceBuilder)));
}
private static String getLogContent(String message) {
return String.format(
"[ip - %s] [path - %s] [req - %s] [user - %s] - %s",
ThreadContext.get("ip"),
ThreadContext.get("uri"),
ThreadContext.get("traceId"),
ThreadContext.get("userName"),
message
);
}
}

View File

@ -0,0 +1,30 @@
package com.vega.hrm.helpers;
import java.util.HashMap;
import org.apache.logging.log4j.ThreadContext;
public class LogHisTemplateHelper {
public static <T> String logContent(String actionType, String feature, T dataOld, T dataNew) {
return String.format(
"[ip - %s] [Thao tác - %s - %s] [user - %s] - %s<br/>---------->>><br/>%s",
ThreadContext.get("ip"),
feature,
actionUserDir.get(actionType),
ThreadContext.get("userName"), CommonHelper.convertObjectToCommaSeparated(dataOld),
CommonHelper.convertObjectToCommaSeparated(dataNew)
);
}
public static HashMap<String, String> actionUserDir =
new HashMap<>() {
{
put("0", "Query");
put("1", "AddNew");
put("2", "Update");
put("3", "Delete");
put("4", "ChangeStatus");
put("5", "Others");
}
};
}

View File

@ -0,0 +1,29 @@
package com.vega.hrm.helpers;
import java.security.SecureRandom;
public class StringHelper {
public static String PW_CHARS = "abcdefghjkmnpqrstuvwxyz";
public static String PW_NUMBERS = "123456789";
public static String PW_SPECIAL_CHARS = "!#$%&()*+,-./:;<=>?@[]^_{}~";
private static final SecureRandom RANDOM = new SecureRandom();
public static String randomStr(String str, int length) {
if (str == null || str.isBlank()) {
return "";
}
length = Math.min(length, str.length());
StringBuilder stringBuilder = new StringBuilder(length);
for (int i = 0; i < length; i++) {
int index = RANDOM.nextInt(str.length());
stringBuilder.append(str.charAt(index));
}
return stringBuilder.toString();
}
}

View File

@ -0,0 +1,18 @@
package com.vega.hrm.models;
import java.time.Instant;
import lombok.Getter;
import lombok.NoArgsConstructor;
import vn.vnpay.dvnh.backoffice.core.common.utility.DateTimeUtils;
@Getter
@NoArgsConstructor
public class RedisEccKeyPair {
private String privateKey;
private String expiration;
public RedisEccKeyPair(String privateKey, Instant expiration) {
this.privateKey = privateKey;
this.expiration = DateTimeUtils.format(expiration);
}
}

View File

@ -0,0 +1,13 @@
package com.vega.hrm.models.requests;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Getter
@NoArgsConstructor
public class BaseDecryptedRequest {
private String request;
private String key;
private String p;
}

View File

@ -0,0 +1,8 @@
package com.vega.hrm.models.requests;
import lombok.Getter;
@Getter
public class BaseRequest {
}

View File

@ -0,0 +1,11 @@
package com.vega.hrm.models.requests;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class ChangeStatusRequest<T> extends BaseRequest {
private T id;
private String status;
}

View File

@ -0,0 +1,19 @@
package com.vega.hrm.models.requests;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@Getter
@Builder
@Setter
@AllArgsConstructor
public class CreateUserHistoryRequest {
private String funcName;
private String actionType;
private String action;
private String oldValue;
private String newValue;
private String editTable;
}

View File

@ -0,0 +1,11 @@
package com.vega.hrm.models.requests;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class GetByIdRequest<T> extends BaseRequest {
private T id;
}

View File

@ -0,0 +1,29 @@
package com.vega.hrm.models.requests;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.Positive;
import lombok.Getter;
import lombok.Setter;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
@Getter
@Setter
public class PagingRequest<T> {
@Min(1)
private int pageIndex;
@Positive
private int pageSize;
private T filter;
public Pageable toPageable(){
return PageRequest.of(this.pageIndex - 1, this.pageSize, Sort.unsorted());
}
public Pageable toPageable(Sort sort){
return PageRequest.of(this.pageIndex - 1, this.pageSize, sort);
}
}

View File

@ -0,0 +1,5 @@
package com.vega.hrm.models.responses;
public record BaseEncryptedResponse(String key, String response) {
}

View File

@ -0,0 +1,57 @@
package com.vega.hrm.models.responses;
import com.vega.hrm.constants.ResponseCodeConst;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.apache.logging.log4j.ThreadContext;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Accessors(chain = true)
public class BaseResponse<T> {
@Builder.Default
private String traceId = ThreadContext.get("traceId");
@Builder.Default
private String code = ResponseCodeConst.SUCCESS;
private String message;
private T data;
public static <T> BaseResponse<T> Success(String msg) {
return BaseResponse.<T>builder()
.code(ResponseCodeConst.SUCCESS)
.message(msg)
.build();
}
public static <T> BaseResponse<T> Invalid(String msg) {
return BaseResponse.<T>builder()
.code(ResponseCodeConst.INVALID)
.message(msg)
.build();
}
public static <T> BaseResponse<T> NotFound(String msg) {
return BaseResponse.<T>builder()
.code(ResponseCodeConst.NOT_FOUND)
.message(msg)
.build();
}
public static <T> BaseResponse<T> InternalSystemError() {
return BaseResponse.<T>builder()
.code(ResponseCodeConst.INTERNAL_SYSTEM_ERROR)
.message("Internal System Error")
.build();
}
}

View File

@ -0,0 +1,21 @@
package com.vega.hrm.models.responses;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.Accessors;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Accessors(chain = true)
public class PagedList<T> {
private int pageCount;
private long totalItemCount;
private List<T> items;
}

View File

@ -0,0 +1,29 @@
package com.vega.hrm.models.responses;
import com.vega.hrm.constants.ResponseCodeConst;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.apache.logging.log4j.ThreadContext;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Accessors(chain = true)
public class PagedListResponse<T> {
@Builder.Default
private String traceId = ThreadContext.get("traceId");
@Builder.Default
private String code = ResponseCodeConst.SUCCESS;
private String message;
private PagedList<T> data;
}

View File

@ -0,0 +1,57 @@
package com.vega.hrm.wrapper;
import jakarta.servlet.ReadListener;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class CustomRequestWrapper extends HttpServletRequestWrapper {
private final byte[] body;
public CustomRequestWrapper(HttpServletRequest request, String bodyString) {
super(request);
this.body = bodyString.getBytes(StandardCharsets.UTF_8);
}
@Override
public ServletInputStream getInputStream() {
return new ServletInputStream() {
private final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body);
@Override
public int read() {
return byteArrayInputStream.read();
}
@Override
public boolean isFinished() {
return byteArrayInputStream.available() == 0;
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setReadListener(ReadListener readListener) {
}
};
}
@Override
public BufferedReader getReader() {
return new BufferedReader(new InputStreamReader(getInputStream(), StandardCharsets.UTF_8));
}
}

View File

@ -0,0 +1,50 @@
package com.vega.hrm.wrapper;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.WriteListener;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletResponseWrapper;
import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
public class CustomResponseWrapper extends HttpServletResponseWrapper {
private final ByteArrayOutputStream byteArrayOutputStream;
private final PrintWriter printWriter;
public CustomResponseWrapper(HttpServletResponse response) {
super(response);
this.byteArrayOutputStream = new ByteArrayOutputStream();
this.printWriter = new PrintWriter(byteArrayOutputStream);
}
@Override
public ServletOutputStream getOutputStream() {
return new ServletOutputStream() {
@Override
public void write(int b) {
byteArrayOutputStream.write(b);
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setWriteListener(WriteListener writeListener) {
}
};
}
@Override
public PrintWriter getWriter() {
return printWriter;
}
public String getContent() {
return byteArrayOutputStream.toString(StandardCharsets.UTF_8);
}
}