@@ -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; | |||||
} | |||||
} | |||||
@@ -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); | |||||
} |
@@ -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; | |||||
} | |||||
} |
@@ -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)); | |||||
} | |||||
} |
@@ -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); | |||||
} | |||||
} |
@@ -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); | |||||
} | |||||
} |
@@ -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; |
@@ -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 ; |