Преглед на файлове

updated edit & save PDF form

master
kelvinsuen преди 1 месец
родител
ревизия
db18330e45
променени са 8 файла, в които са добавени 502 реда и са изтрити 86 реда
  1. +69
    -0
      src/main/java/com/ffii/lioner/modules/lioner/pdf/entity/Pdf.java
  2. +10
    -0
      src/main/java/com/ffii/lioner/modules/lioner/pdf/entity/PdfRepository.java
  3. +47
    -0
      src/main/java/com/ffii/lioner/modules/lioner/pdf/req/UpdatePdfReq.java
  4. +237
    -0
      src/main/java/com/ffii/lioner/modules/lioner/pdf/service/PdfService.java
  5. +112
    -0
      src/main/java/com/ffii/lioner/modules/lioner/pdf/web/PdfController.java
  6. +0
    -86
      src/main/java/com/ffii/lioner/modules/lioner/web/PdfController.java
  7. +20
    -0
      src/main/resources/db/changelog/changes/05_filled_form/01_add_filled_form_table.sql
  8. +7
    -0
      src/main/resources/db/changelog/changes/05_filled_form/02_update_client_table.sql

+ 69
- 0
src/main/java/com/ffii/lioner/modules/lioner/pdf/entity/Pdf.java Целия файл

@@ -0,0 +1,69 @@
package com.ffii.lioner.modules.lioner.pdf.entity;

import java.time.LocalDate;
import java.time.LocalDateTime;

import com.ffii.core.entity.BaseEntity;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Inheritance;
import jakarta.persistence.InheritanceType;
import jakarta.persistence.Table;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;

@Entity
@Table(name = "filled_form")
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public class Pdf extends BaseEntity<Long>{

@NotNull
@Column
private Long templateId;
@Column
private Long clientId;
@NotNull
@Column
private Long fileId;
@Column
private String remarks;


public Long getTemplateId() {
return templateId;
}

public void setTemplateId(Long templateId) {
this.templateId = templateId;
}

public Long getClientId() {
return clientId;
}

public void setClientId(Long clientId) {
this.clientId = clientId;
}

public Long getFileId() {
return fileId;
}

public void setFileId(Long fileId) {
this.fileId = fileId;
}

public String getRemarks() {
return remarks;
}

public void setRemarks(String remarks) {
this.remarks = remarks;
}

}


+ 10
- 0
src/main/java/com/ffii/lioner/modules/lioner/pdf/entity/PdfRepository.java Целия файл

@@ -0,0 +1,10 @@
package com.ffii.lioner.modules.lioner.pdf.entity;

import java.util.List;
// import java.util.Map;

import com.ffii.core.support.AbstractRepository;

public interface PdfRepository extends AbstractRepository<Pdf, Long> {
List<Pdf> findAllByClientId(Long clientId);
}

+ 47
- 0
src/main/java/com/ffii/lioner/modules/lioner/pdf/req/UpdatePdfReq.java Целия файл

@@ -0,0 +1,47 @@
package com.ffii.lioner.modules.lioner.pdf.req;

public class UpdatePdfReq {
private Long id;
private Long templateId;

private Long clientId;

private String remarks;

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public Long getTemplateId() {
return templateId;
}

public void setTemplateId(Long templateId) {
this.templateId = templateId;
}

public Long getClientId() {
return clientId;
}

public void setClientId(Long clientId) {
this.clientId = clientId;
}

public String getRemarks() {
return remarks;
}

public void setRemarks(String remarks) {
this.remarks = remarks;
}


}

+ 237
- 0
src/main/java/com/ffii/lioner/modules/lioner/pdf/service/PdfService.java Целия файл

@@ -0,0 +1,237 @@
package com.ffii.lioner.modules.lioner.pdf.service;

import java.io.IOException;
import java.io.InputStream;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.ClassPathResource;

import com.ffii.lioner.modules.lioner.pdf.entity.Pdf;
import com.ffii.lioner.modules.lioner.pdf.entity.PdfRepository;
import com.ffii.lioner.modules.lioner.entity.ImpEvent;
import com.ffii.lioner.modules.lioner.pdf.req.UpdatePdfReq;
import com.ffii.lioner.modules.lioner.service.FileService;
import com.ffii.lioner.modules.common.SecurityUtils;
import com.ffii.lioner.modules.common.service.AuditLogService;
import com.ffii.lioner.modules.master.entity.SubDivision;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ffii.core.exception.NotFoundException;
import com.ffii.core.exception.UnprocessableEntityException;
import com.ffii.core.support.AbstractBaseEntityService;
import com.ffii.core.support.JdbcDao;
import com.ffii.core.utils.BeanUtils;
import com.ffii.core.utils.JsonUtils;
import com.ffii.core.utils.Params;

import jakarta.persistence.Table;

@Service
public class PdfService extends AbstractBaseEntityService<Pdf, Long, PdfRepository> {

private String pdf_path = "templates/pdf/";
private AuditLogService auditLogService;
private FileService fileService;
public PdfService(JdbcDao jdbcDao, PdfRepository repository, AuditLogService auditLogService, FileService fileService) {
super(jdbcDao, repository);
this.auditLogService = auditLogService;
this.fileService = fileService;
}

// public Map<String,Object> getAuditLogObject(Map<String,Object> req){
// StringBuilder sql = new StringBuilder("SELECT"
// + " e.id, "
// + " e.created, "
// + " e.createdBy AS createdById, "
// + " u1.name AS createdBy, "
// + " e.version, "
// + " e.modified, "
// + " e.modifiedBy AS modifiedById, "
// + " e.deleted, "
// + " u2.name AS modifiedBy, "
// + " e.name, "
// + " e.nameCht, "
// + " e.description, "
// + " e.region, "
// + " e.organization, "
// + " e.eventType, "
// + " e.frequency, "
// + " e.series, "
// + " e.startDate, "
// + " e.eventFrom, "
// + " e.eventTo, "
// + " e.applicationDeadline, "
// + " e.nextApplicationDate, "
// + " e.announcementDate, "
// + " e.awardDate, "
// + " e.reminderFlag, "
// + " e.reminderThreshold, "
// + " e.reminderInterval, "
// + " e.reminderLimit "
// + " FROM event e "
// + " LEFT JOIN `user` u1 ON u1.id = e.createdBy "
// + " LEFT JOIN `user` u2 ON u2.id = e.modifiedBy "
// + " WHERE e.id = :id "
// );
// return jdbcDao.queryForMap(sql.toString(), req).get();
// }

public Map<String, Object> saveOrUpdate(String recordJson, MultipartFile file) {
ObjectMapper objectMapper = new ObjectMapper();
UpdatePdfReq req;
try {
req = objectMapper.readValue(recordJson, UpdatePdfReq.class);
} catch (Exception e) {
throw new UnprocessableEntityException("Invalid JSON format");
}
Pdf instance;
// List<SubDivision> onUsingIdList = new ArrayList<SubDivision>();
if(req.getId()>0){
instance = find(req.getId()).get();
fileService.deleteFile(instance.getFileId()); // Delete previous record - WIP
// onUsingIdList = this.getSelectedSubDivisionList(req.getId());
}
else{
instance = new Pdf();
}
System.out.println(req.getTemplateId());
BeanUtils.copyProperties(req, instance);

Map<String, Object> pdf = new HashMap<>();
Map<String, Object> response = new HashMap<>();

if (file.isEmpty()) {
throw new UnprocessableEntityException("No file uploaded.");
// return ResponseEntity.badRequest().body(Map.of("message", "No file uploaded."));
}
try {
// Convert the MultipartFile to a byte array
byte[] pdfData = file.getBytes();

MultipartFile[] files = new MultipartFile[1];
files[0] = file;

try {
pdf = fileService.saveOrUpdate(files, (long) 0, "pdf");
} catch (Exception e) {
System.out.println(e);
}

// Save the PDF data to the database
// File pdfForm = new PdfForm();
// pdfForm.setPdfData(pdfData);
// pdfFormRepository.save(pdfForm); // Assuming you have autowired this repository

response.put("message", "PDF saved successfully.");
// response.put("id", pdfForm.getId().toString()); // Return the ID of the saved PDF

} catch (IOException e) {
throw new UnprocessableEntityException(
"Failed to save PDF: "
+ e
);
// e.printStackTrace();
// return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
// .body(Map.of("message", "Failed to save PDF: " + e.getMessage()));
}
Object fileIdList = pdf.get("fileIdList");
List<?> list = (List<?>) fileIdList;
Long fileId = (long) list.get(0);

instance.setFileId(fileId);
instance = save(instance);

response = Map.of(
"id", instance.getId()
);

return response;
}

public byte[] getTemplateForm() {
// Ensure the template PDF is in src/main/resources/template/pdf
ClassPathResource pdfFile = new ClassPathResource(pdf_path + "HSBC B24102883_fillable_Financial Needs Analysis for Individual Nov2024.pdf");
if (!pdfFile.exists()) {
throw new UnprocessableEntityException("PDF template not found.");
}
try (InputStream is = pdfFile.getInputStream()) {
return is.readAllBytes();
} catch (Exception e) {
throw new UnprocessableEntityException("Fail to load PDF: " + e);
}

// return fileService.getfileBlob((long) 2);
}

public List<Map<String,Object>> list(Map<String, Object> args){
StringBuilder sql = new StringBuilder("SELECT"
+ " id, "
+ " templateId, "
+ " fileId, "
+ " created, "
+ " modified "
+ " FROM filled_form "
+ " WHERE deleted = FALSE "
);

if (args != null) {
if (args.containsKey("clientId"))
sql.append(" AND clientId = :clientId ");
if (args.containsKey("templateId"))
sql.append(" AND templateId = :templateId ");
if (args.containsKey("remarks"))
sql.append(" AND remarks LIKE :remarks ");
if (args.containsKey("createDateFrom"))
sql.append(" AND created >= :createDateFrom ");
if (args.containsKey("createDateTo"))
sql.append(" AND created <= :createDateTo ");
}
// sql.append(" ORDER BY id desc ");

return jdbcDao.queryForList(sql.toString(), args);
}

public Map<String, Object> loadPDF(Long id) {
// Pdf pdfInstance = find(id).orElseThrow(NotFoundException::new);
// byte[] blobBytes = fileService.getfileBlob(pdfInstance.getFileId());

StringBuilder sql = new StringBuilder("SELECT"
+ " ff.*, "
+ " fb.bytes AS blobValue"
+ " FROM filled_form ff "
+ " LEFT JOIN file_blob fb ON ff.fileId = fb.fileId "
+ " WHERE ff.deleted = FALSE"
+ " AND ff.id = :id "
);
// Map<String, Object> result = jdbcDao.queryForMap(sql.toString(), Map.of("id", id)).orElseThrow(NotFoundException::new);
// result.put("blobValue", new ByteArrayResource(blobBytes));
// return result;
return jdbcDao.queryForMap(sql.toString(), Map.of("id", id)).orElseThrow(NotFoundException::new);
}
public Integer getIdByName(String name) {
StringBuilder sql = new StringBuilder("SELECT"
+ " e.id "
+ " FROM event e "
+ " WHERE e.deleted = FALSE"
+ " AND e.name = :name "
);
return jdbcDao.queryForInt(sql.toString(), Map.of("name", name));
}
}

+ 112
- 0
src/main/java/com/ffii/lioner/modules/lioner/pdf/web/PdfController.java Целия файл

@@ -0,0 +1,112 @@
package com.ffii.lioner.modules.lioner.pdf.web;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import com.ffii.core.response.DataRes;
import com.ffii.core.response.RecordsRes;
import com.ffii.core.utils.CriteriaArgsBuilder;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
import com.ffii.core.exception.NotFoundException;
import com.ffii.core.utils.Params;

import com.ffii.lioner.modules.lioner.pdf.req.UpdatePdfReq;
import com.ffii.lioner.modules.lioner.pdf.service.PdfService;


@RestController
@RequestMapping("/pdf")
@CrossOrigin(origins = "", allowedHeaders = "")

public class PdfController {

private PdfService pdfService;

public PdfController(PdfService pdfService) {
this.pdfService = pdfService;
}

// @GetMapping("/{id}")
// public List<Map<String, Object>> get(@PathVariable Long id) {
// return Map.of(
// Params.DATA, pdfService.findByClientId(id).orElseThrow(NotFoundException::new)
// );
// }

@GetMapping
public RecordsRes<Map<String, Object>> list(HttpServletRequest request) throws ServletRequestBindingException {
return new RecordsRes<>(pdfService.list(
CriteriaArgsBuilder.withRequest(request)
.addInteger("clientId")
.addInteger("templateId")
.addDate("createDateFrom")
.addDate("createDateTo")
.addStringLike("remarks")
.build()));
}

@GetMapping("/{id}")
public Map<String, Object> get(@PathVariable Long id) {
return pdfService.loadPDF(id);
// return Map.of(
// Params.DATA, pdfService.loadPDF(id)
// );
}

// Endpoint to serve the initial template PDF
@GetMapping(value = "/template", produces = MediaType.APPLICATION_PDF_VALUE)
public ResponseEntity<byte[]> getPdfTemplate() throws IOException {
byte[] pdfBytes = pdfService.getTemplateForm();

HttpHeaders headers = new HttpHeaders();
headers.setContentDispositionFormData("inline", "template_form.pdf");
headers.setContentType(MediaType.APPLICATION_PDF);
return new ResponseEntity<>(pdfBytes, headers, HttpStatus.OK);
}

// Endpoint to receive the filled PDF from the frontend
@Transactional(isolation = Isolation.SERIALIZABLE, rollbackFor = Exception.class, readOnly = false)
@PostMapping(value = "/save", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
// public Map<String, Object> saveFilledPdf(@RequestParam("pdf") @Valid UpdatePdfReq req, @RequestParam("file") MultipartFile file) {
// public Map<String, Object> saveFilledPdf(@RequestParam("record") UpdatePdfReq req, @RequestParam("file") MultipartFile file) {
public Map<String, Object> saveFilledPdf(@RequestParam("record") String recordJson, @RequestParam("file") MultipartFile file) {
return Map.of(
Params.DATA,pdfService.saveOrUpdate(recordJson, file)
);
}

// Optional: Endpoint to receive just the form data (e.g., JSON)
@PostMapping("/saveFormData")
public ResponseEntity<Map<String, String>> saveFormData(@RequestBody Map<String, Object> formData) {
System.out.println("Received form data: " + formData);
Map<String, String> response = new HashMap<>();
response.put("message", "Form data received and processed successfully.");
return ResponseEntity.ok(response);
}
}

+ 0
- 86
src/main/java/com/ffii/lioner/modules/lioner/web/PdfController.java Целия файл

@@ -1,86 +0,0 @@
package com.ffii.lioner.modules.lioner.web;

import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import org.springframework.core.io.ClassPathResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

@RestController
@RequestMapping("/pdf")
@CrossOrigin(origins = "", allowedHeaders = "")

public class PdfController {

private String pdf_path = "templates/pdf/";
// Endpoint to serve the initial template PDF
@GetMapping(value = "/template", produces = MediaType.APPLICATION_PDF_VALUE)
public ResponseEntity<byte[]> getPdfTemplate() throws IOException {
// Ensure your template PDF is in src/main/resources/static/
ClassPathResource pdfFile = new ClassPathResource(pdf_path + "HSBC B24102883_fillable_Financial Needs Analysis for Individual Nov2024.pdf");
if (!pdfFile.exists()) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body("PDF template not found.".getBytes());
}
byte[] pdfBytes;
try (InputStream is = pdfFile.getInputStream()) {
pdfBytes = is.readAllBytes();
}

HttpHeaders headers = new HttpHeaders();
headers.setContentDispositionFormData("inline", "template_form.pdf");
headers.setContentType(MediaType.APPLICATION_PDF);
return new ResponseEntity<>(pdfBytes, headers, HttpStatus.OK);
}

// Endpoint to receive the filled PDF from the frontend
@PostMapping(value = "/save", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<Map<String, String>> saveFilledPdf(@RequestParam("file") MultipartFile file) {
if (file.isEmpty()) {
return ResponseEntity.badRequest().body(Map.of("message", "No file uploaded."));
}
try {
// Generate a unique filename and save to an 'uploads' directory
String fileName = "filled_form_" + UUID.randomUUID().toString() + ".pdf";
Path filePath = Paths.get("uploads", fileName);
Files.createDirectories(filePath.getParent()); // Ensure directory exists
Files.copy(file.getInputStream(), filePath);

Map<String, String> response = new HashMap<>();
response.put("message", "PDF saved successfully: " + fileName);
response.put("filePath", filePath.toString());
return ResponseEntity.ok(response);

} catch (IOException e) {
e.printStackTrace();
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(Map.of("message", "Failed to save PDF: " + e.getMessage()));
}
}

// Optional: Endpoint to receive just the form data (e.g., JSON)
@PostMapping("/saveFormData")
public ResponseEntity<Map<String, String>> saveFormData(@RequestBody Map<String, Object> formData) {
System.out.println("Received form data: " + formData);
Map<String, String> response = new HashMap<>();
response.put("message", "Form data received and processed successfully.");
return ResponseEntity.ok(response);
}
}

+ 20
- 0
src/main/resources/db/changelog/changes/05_filled_form/01_add_filled_form_table.sql Целия файл

@@ -0,0 +1,20 @@
--liquibase formatted sql

--changeset kelvin:add filled form table

CREATE TABLE `filled_form` (
`id` int NOT NULL AUTO_INCREMENT,
`created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`createdBy` int DEFAULT NULL,
`version` int NOT NULL DEFAULT '0',
`modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`modifiedBy` int DEFAULT NULL,
`deleted` tinyint(1) NOT NULL DEFAULT '0',

`templateId` int(11) NOT NULL,
`clientId` int(11),
`fileId` int(11) NOT NULL,
`remarks` VARCHAR(600),
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

+ 7
- 0
src/main/resources/db/changelog/changes/05_filled_form/02_update_client_table.sql Целия файл

@@ -0,0 +1,7 @@
--liquibase formatted sql

--changeset kelvin:update client table

ALTER TABLE `lionerdb`.`client`
CHANGE COLUMN `createdBy` `createdBy` INT NULL DEFAULT NULL ,
CHANGE COLUMN `modifiedBy` `modifiedBy` INT NULL DEFAULT NULL ;

Зареждане…
Отказ
Запис