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