중간저장.

master
강석 최 2023-06-05 18:25:02 +09:00
parent 9fba2e5d79
commit d0c16c907e
12 changed files with 219 additions and 50 deletions

View File

@ -36,18 +36,10 @@ public class UnlawfulFishingController {
AccessConfig accessConfig = authMgtService.selectAccessConfigList(loginUser.getUserSeq(), "/unlawfulFishing/crackdownInfo").get(0); AccessConfig accessConfig = authMgtService.selectAccessConfigList(loginUser.getUserSeq(), "/unlawfulFishing/crackdownInfo").get(0);
mav.addObject("menuKey", accessConfig.getMenuKey()); mav.addObject("menuKey", accessConfig.getMenuKey());
mav.addObject("accessAuth", accessConfig.getAccessAuth()); mav.addObject("accessAuth", accessConfig.getAccessAuth());
params.setQueryInfo();
if(params.getYear()==null){ mav.addObject("crackdownInfoList", unlawfulFishingService.selectCrackdownInfoList(params));
params.setYear(LocalDateTime.now().getYear()); params.setContentCnt(unlawfulFishingService.selectCrackdownInfoListCnt(params));
} params.setPaginationInfo();
List<Integer> yearList = unlawfulFishingService.selectFishingBoatYearParam();
if(!yearList.contains(params.getYear())){
yearList.add(params.getYear());
}
List<CrackdownStatusDTO> csDTOList = new ArrayList<>();
mav.addObject("crackdownInfoList", csDTOList);
mav.addObject("yearList", yearList);
mav.addObject("searchParams", params); mav.addObject("searchParams", params);
return mav; return mav;
} }
@ -69,9 +61,9 @@ public class UnlawfulFishingController {
return mav; return mav;
} }
@PostMapping("/cdiSave") @PostMapping("/saveCrackdownInfo")
public void cdiSave(CrackdownInfo crackdownInfo){ public Integer saveCrackdownInfo(CrackdownInfo crackdownInfo){
System.out.println(crackdownInfo.getCdsKey()); return unlawfulFishingService.saveCrackdownInfo(crackdownInfo);
} }
@GetMapping("/cdiSelectModal") @GetMapping("/cdiSelectModal")

View File

@ -1,5 +1,7 @@
package com.dbnt.faisp.main.faStatistics.unlawfulFishing.mapper; package com.dbnt.faisp.main.faStatistics.unlawfulFishing.mapper;
import com.dbnt.faisp.main.faStatistics.unlawfulFishing.model.UnlawfulFishingParam;
import com.dbnt.faisp.main.faStatistics.unlawfulFishing.model.crackdownStatus.CrackdownInfo;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import java.util.List; import java.util.List;
@ -8,4 +10,8 @@ import java.util.List;
public interface UnlawfulFishingMapper { public interface UnlawfulFishingMapper {
List<Integer> selectFishingBoatYearParam(); List<Integer> selectFishingBoatYearParam();
List<CrackdownInfo> selectCrackdownInfoList(UnlawfulFishingParam params);
Integer selectCrackdownInfoListCnt(UnlawfulFishingParam params);
} }

View File

@ -100,6 +100,6 @@ public class CrackdownInfoBaseEntity extends BaseModel {
private String wrtUserNm; private String wrtUserNm;
@Column(name = "wrt_dt") @Column(name = "wrt_dt")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm") @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime wrtDt; private LocalDateTime wrtDt;
} }

View File

@ -17,12 +17,12 @@ import java.io.Serializable;
@Table(name = "violation_info") @Table(name = "violation_info")
@IdClass(ViolationInfo.ViolationInfoId.class) @IdClass(ViolationInfo.ViolationInfoId.class)
public class ViolationInfo extends BaseModel { public class ViolationInfo extends BaseModel {
@Id
@Column(name = "violation_key")
private Integer violationKey;
@Id @Id
@Column(name = "cds_key") @Column(name = "cds_key")
private Integer cdsKey; private Integer cdsKey;
@Id
@Column(name = "violation_key")
private Integer violationKey;
@Column(name = "violation") @Column(name = "violation")
private String violation; private String violation;
@ -34,7 +34,7 @@ public class ViolationInfo extends BaseModel {
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public static class ViolationInfoId implements Serializable{ public static class ViolationInfoId implements Serializable{
private Integer violationKey;
private Integer cdsKey; private Integer cdsKey;
private Integer violationKey;
} }
} }

View File

@ -14,18 +14,18 @@ import java.io.Serializable;
@NoArgsConstructor @NoArgsConstructor
@DynamicInsert @DynamicInsert
@DynamicUpdate @DynamicUpdate
@Table(name = "violation_info_version") @Table(name = "violation_info_history")
@IdClass(ViolationInfoVersion.ViolationInfoVersionId.class) @IdClass(ViolationInfoHistory.ViolationInfoHistoryId.class)
public class ViolationInfoVersion extends BaseModel { public class ViolationInfoHistory extends BaseModel {
@Id
@Column(name = "violation_key")
private Integer violationKey;
@Id @Id
@Column(name = "cds_key") @Column(name = "cds_key")
private Integer cdsKey; private Integer cdsKey;
@Id @Id
@Column(name = "version_no") @Column(name = "version_no")
private Integer versionNo; private Integer versionNo;
@Id
@Column(name = "violation_key")
private Integer violationKey;
@Column(name = "violation") @Column(name = "violation")
private String violation; private String violation;
@ -33,9 +33,9 @@ public class ViolationInfoVersion extends BaseModel {
@Data @Data
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public static class ViolationInfoVersionId implements Serializable{ public static class ViolationInfoHistoryId implements Serializable{
private Integer violationKey;
private Integer cdsKey; private Integer cdsKey;
private Integer versionNo; private Integer versionNo;
private Integer violationKey;
} }
} }

View File

@ -0,0 +1,13 @@
package com.dbnt.faisp.main.faStatistics.unlawfulFishing.repository;
import com.dbnt.faisp.main.faStatistics.unlawfulFishing.model.crackdownStatus.ViolationInfoHistory;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
import java.util.Optional;
public interface ViolationInfoHistoryRepository extends JpaRepository<ViolationInfoHistory, ViolationInfoHistory.ViolationInfoHistoryId> {
Optional<ViolationInfoHistory> findTop1ByCdsKeyOrderByVersionNoDesc(Integer cdsKey);
List<ViolationInfoHistory> findByCdsKeyAndVersionNoOrderByViolationKeyAsc(Integer cdsKey, Integer versionNo);
}

View File

@ -0,0 +1,22 @@
package com.dbnt.faisp.main.faStatistics.unlawfulFishing.repository;
import com.dbnt.faisp.main.faStatistics.unlawfulFishing.model.crackdownStatus.ViolationInfo;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Optional;
public interface ViolationInfoRepository extends JpaRepository<ViolationInfo, ViolationInfo.ViolationInfoId> {
List<ViolationInfo> findByCdsKey(Integer cdsKey);
Optional<ViolationInfo> findTopByCdsKeyOrderByViolationKeyDesc(int fbKey);
void deleteByCdsKey(Integer fbKey);
@Transactional
@Modifying
@Query("delete from ViolationInfo v where v.violationKey in :idList")
void deleteAllByIdInQuery(@Param("idList") List<Integer> violationDeleteKeyList);
}

View File

@ -1,8 +1,7 @@
package com.dbnt.faisp.main.faStatistics.unlawfulFishing.service; package com.dbnt.faisp.main.faStatistics.unlawfulFishing.service;
import com.dbnt.faisp.main.faStatistics.unlawfulFishing.model.crackdownStatus.CrackdownInfo; import com.dbnt.faisp.main.faStatistics.unlawfulFishing.model.UnlawfulFishingParam;
import com.dbnt.faisp.main.faStatistics.unlawfulFishing.model.crackdownStatus.CrackdownStatus; import com.dbnt.faisp.main.faStatistics.unlawfulFishing.model.crackdownStatus.*;
import com.dbnt.faisp.main.faStatistics.unlawfulFishing.model.crackdownStatus.UnlawfulFishingVersion;
import com.dbnt.faisp.main.faStatistics.unlawfulFishing.model.fishingBoat.FishingBoat; import com.dbnt.faisp.main.faStatistics.unlawfulFishing.model.fishingBoat.FishingBoat;
import com.dbnt.faisp.main.faStatistics.unlawfulFishing.model.fishingBoat.IllegalShipInfo; import com.dbnt.faisp.main.faStatistics.unlawfulFishing.model.fishingBoat.IllegalShipInfo;
import com.dbnt.faisp.main.faStatistics.unlawfulFishing.model.processResult.ShipProcessInfo; import com.dbnt.faisp.main.faStatistics.unlawfulFishing.model.processResult.ShipProcessInfo;
@ -11,10 +10,12 @@ import com.dbnt.faisp.main.faStatistics.unlawfulFishing.model.sailor.Sailor;
import com.dbnt.faisp.main.faStatistics.unlawfulFishing.mapper.UnlawfulFishingMapper; import com.dbnt.faisp.main.faStatistics.unlawfulFishing.mapper.UnlawfulFishingMapper;
import com.dbnt.faisp.main.faStatistics.unlawfulFishing.repository.*; import com.dbnt.faisp.main.faStatistics.unlawfulFishing.repository.*;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List; import java.util.List;
@Service @Service
@ -27,6 +28,8 @@ public class UnlawfulFishingService {
private final CrackdownInfoRepository cdiRepository; private final CrackdownInfoRepository cdiRepository;
private final CrackdownInfoHistoryRepository cdihRepository; private final CrackdownInfoHistoryRepository cdihRepository;
private final ViolationInfoRepository viRepository;
private final ViolationInfoHistoryRepository vihRepository;
private final IllegalShipInfoRepository isiRepository; private final IllegalShipInfoRepository isiRepository;
private final IllegalShipInfoHistoryRepository isihRepository; private final IllegalShipInfoHistoryRepository isihRepository;
private final ShipProcessInfoRepository spiRepository; private final ShipProcessInfoRepository spiRepository;
@ -93,10 +96,41 @@ public class UnlawfulFishingService {
} }
return crackdownStatus; return crackdownStatus;
} }
public List<Integer> selectFishingBoatYearParam() { public List<Integer> selectFishingBoatYearParam() {
return unlawfulFishingMapper.selectFishingBoatYearParam(); return unlawfulFishingMapper.selectFishingBoatYearParam();
} }
@Transactional
public Integer saveCrackdownInfo(CrackdownInfo crackdownInfo) {
Integer cdsKey = cdiRepository.save(crackdownInfo).getCdsKey();
List<ViolationInfo> violationInfoList = crackdownInfo.getViolationList();
int i=1;
for(ViolationInfo violationInfo : violationInfoList){
violationInfo.setCdsKey(cdsKey);
violationInfo.setViolationKey(i++);
}
viRepository.saveAll(violationInfoList);
if(!crackdownInfo.getStatus().equals("DST001")){
CrackdownInfoHistory lastHistory = cdihRepository.findTopByCdsKeyOrderByVersionNoDesc(cdsKey).orElse(null);
CrackdownInfoHistory cdihistory = new CrackdownInfoHistory();
BeanUtils.copyProperties(crackdownInfo, cdihistory);
cdihistory.setVersionNo(lastHistory==null?1:lastHistory.getVersionNo()+1);
cdihRepository.save(cdihistory);
List<ViolationInfoHistory> viHistoryList = new ArrayList<>();
for(ViolationInfo vi: violationInfoList){
ViolationInfoHistory vih = new ViolationInfoHistory();
BeanUtils.copyProperties(vi, vih);
vih.setVersionNo(cdihistory.getVersionNo());
viHistoryList.add(vih);
}
vihRepository.saveAll(viHistoryList);
}
return cdsKey;
}
public CrackdownInfo selectCrackdownInfo(Integer cdsKey) { public CrackdownInfo selectCrackdownInfo(Integer cdsKey) {
return cdiRepository.findByCdsKey(cdsKey).orElse(new CrackdownInfo()); return cdiRepository.findByCdsKey(cdsKey).orElse(new CrackdownInfo());
} }
@ -112,4 +146,11 @@ public class UnlawfulFishingService {
public IllegalShipSailor selectIllegalShipSailor(Integer sailorKey) { public IllegalShipSailor selectIllegalShipSailor(Integer sailorKey) {
return issRepository.findBySailorKey(sailorKey).orElse(new IllegalShipSailor()); return issRepository.findBySailorKey(sailorKey).orElse(new IllegalShipSailor());
} }
public List<CrackdownInfo> selectCrackdownInfoList(UnlawfulFishingParam params) {
return unlawfulFishingMapper.selectCrackdownInfoList(params);
}
public Integer selectCrackdownInfoListCnt(UnlawfulFishingParam params) {
return unlawfulFishingMapper.selectCrackdownInfoListCnt(params);
}
} }

View File

@ -11,4 +11,55 @@
where napo_dt is not null where napo_dt is not null
</select> </select>
<select id="selectCrackdownInfoList" resultType="CrackdownInfo" parameterType="UnlawfulFishingParam">
select a.cds_key ,
case_num ,
napo_dt ,
napo_sea_point_lon ,
napo_sea_point_lat ,
napo_sea_point_detail ,
invasion_type ,
nll ,
case_agency ,
case_police_officer ,
crackdown_police ,
crackdown_boat ,
mmsi ,
field_ivsgt ,
wrt_organ ,
wrt_part ,
wrt_user_grd ,
wrt_user_nm ,
wrt_dt ,
violationStr,
violationCode
from crackdown_info a
left outer join (
select aa.cds_key,
array_to_string(array_agg(ab.item_value), ', ') as violationStr,
array_to_string(array_agg(ab.item_cd), ', ') as violationCode
from violation_info aa
inner join code_mgt ab on aa.violation = ab.item_cd
group by aa.cds_key
) vi on a.cds_key = vi.cds_key
<include refid="selectCrackdownInfoListWhere"></include>
order by a.wrt_dt desc
limit #{rowCnt} offset #{firstIndex}
</select>
<select id="selectCrackdownInfoListCnt" resultType="int" parameterType="UnlawfulFishingParam">
select count(*)
from crackdown_info a
left outer join (
select aa.cds_key,
array_to_string(array_agg(ab.item_value), ', ') as violationStr,
array_to_string(array_agg(ab.item_cd), ', ') as violationCode
from violation_info aa
inner join code_mgt ab on aa.violation = ab.item_cd
group by aa.cds_key
) vi on a.cds_key = vi.cds_key
<include refid="selectCrackdownInfoListWhere"></include>
</select>
<sql id="selectCrackdownInfoListWhere">
where status &lt;> 'DST008'
</sql>
</mapper> </mapper>

View File

@ -28,7 +28,18 @@ $(document).on('change', '#violationSelector', function (){
break; break;
} }
}) })
$(document).on('click', '.violationRemoveBtn', function (){
const parentDiv = $(this).parents(".violation")
switch (parentDiv.find(".violationCd").val()){
case "VT011": // 어획량 축소 기재
childInputStateChange(1, $("#catchDiv").find("input"))
break;
case "VT028": // 특수공무집행방해
childInputStateChange(1, $("#damageDiv").find("input, textarea"))
break;
}
parentDiv.remove();
})
$(document).on('change', '#crackdownPolice', function (){ $(document).on('change', '#crackdownPolice', function (){
getChildOption(this.value, null, "#crackdownBoat"); getChildOption(this.value, null, "#crackdownBoat");
}); });
@ -75,9 +86,9 @@ function childInputStateChange(selectorValue, inputs){
function saveCrackdownInfo(status) { function saveCrackdownInfo(status) {
if (confirm("저장하시겠습니까?")) { if (confirm("저장하시겠습니까?")) {
if (valueCheck(status)) { if (valueCheck(status)) {
$(".infoStatus").val(status) $("#infoStatus").val(status)
contentFade("in"); contentFade("in");
const formData = new FormData($("#csEditForm")[0]); const formData = new FormData($("#cdiEditForm")[0]);
$.each($(".violationCd"), function (idx, input){ $.each($(".violationCd"), function (idx, input){
formData.append('violationList['+idx+'].violation', $(input).val()); formData.append('violationList['+idx+'].violation', $(input).val());
}); });
@ -85,7 +96,7 @@ function saveCrackdownInfo(status) {
$.ajax({ $.ajax({
type : 'POST', type : 'POST',
data : formData, data : formData,
url : "/unlawfulFishing/cdiSave", url : "/unlawfulFishing/saveCrackdownInfo",
processData: false, processData: false,
contentType: false, contentType: false,
beforeSend: function (xhr){ beforeSend: function (xhr){
@ -107,27 +118,28 @@ function saveCrackdownInfo(status) {
function valueCheck(status){ function valueCheck(status){
if(status === "DST007"){ if(status === "DST007"){
if(!$("#caseNum").val()){ const cdiEditForm = $("#cdiEditForm")
if(!cdiEditForm.find("#caseNum").val()){
alert("사건번호를 입력해주세요") alert("사건번호를 입력해주세요")
return false; return false;
} }
if($(".violationCd").length<1){ if(cdiEditForm.find(".violationCd").length<1){
alert("위반사항을 선택해주세요") alert("위반사항을 선택해주세요")
return false; return false;
} }
if(!$("#caseAgency").val()){ if(!cdiEditForm.find("#caseAgency").val()){
alert("사건담당경찰서를 선택해주세요") alert("사건담당경찰서를 선택해주세요")
return false; return false;
} }
if(!$("#casePoliceOfficer").val()){ if(!cdiEditForm.find("#casePoliceOfficer").val()){
alert("사건담당경찰관을 입력해주세요") alert("사건담당경찰관을 입력해주세요")
return false; return false;
} }
if(!$("#crackdownPolice").val()){ if(!cdiEditForm.find("#crackdownPolice").val()){
alert("단속경찰서를 선택해주세요") alert("단속경찰서를 선택해주세요")
return false; return false;
} }
if(!$("#crackdownBoat").val()){ if(!cdiEditForm.find("#crackdownBoat").val()){
alert("단속함정을 선택해주세요") alert("단속함정을 선택해주세요")
return false; return false;
} }

View File

@ -5,7 +5,7 @@
<button type="button" class="btn-close f-invert" data-bs-dismiss="modal" aria-label="Close"></button> <button type="button" class="btn-close f-invert" data-bs-dismiss="modal" aria-label="Close"></button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<form action="#" method="post" id="csEditForm"> <form action="#" method="post" id="cdiEditForm">
<input type="hidden" name="cdsKey" id="cdsKey" th:value="${crackdownInfo.cdsKey}"> <input type="hidden" name="cdsKey" id="cdsKey" th:value="${crackdownInfo.cdsKey}">
<input type="hidden" class="status" name="status" id="infoStatus" th:value="${crackdownInfo.status}"> <input type="hidden" class="status" name="status" id="infoStatus" th:value="${crackdownInfo.status}">
<input type="hidden" name="wrtOrgan" th:value="${crackdownInfo.wrtOrgan}"> <input type="hidden" name="wrtOrgan" th:value="${crackdownInfo.wrtOrgan}">
@ -59,7 +59,7 @@
<div class="row mb-1"> <div class="row mb-1">
<label for="caseAgency" class="col-sm-1 col-form-label col-form-label-sm text-center">사건담당경찰서</label> <label for="caseAgency" class="col-sm-1 col-form-label col-form-label-sm text-center">사건담당경찰서</label>
<div class="col-sm-2"> <div class="col-sm-2">
<select class="form-select form-select-sm " id="caseAgency" name="caseAgency"> <select class="form-select form-select-sm" id="caseAgency" name="caseAgency">
<option value="">선택</option> <option value="">선택</option>
<th:block th:each="code:${session.commonCode.get('OG')}"> <th:block th:each="code:${session.commonCode.get('OG')}">
<option th:value="${code.itemCd}" th:text="${code.itemValue}" th:selected="${code.itemCd eq crackdownInfo.caseAgency}"></option> <option th:value="${code.itemCd}" th:text="${code.itemValue}" th:selected="${code.itemCd eq crackdownInfo.caseAgency}"></option>

View File

@ -118,15 +118,47 @@
</tr> </tr>
</thead> </thead>
<tbody class="table-group-divider align-middle"> <tbody class="table-group-divider align-middle">
<th:block th:each="dto,cnt:${crackdownInfoList}"> <th:block th:each="cdi,cnt:${crackdownInfoList}">
<tr class="crackdownStatusTr" th:data-cdskey="${dto.cdsKey}" data-modaltype="viewOnly"> <tr class="crackdownStatusTr" th:data-cdskey="${cdi.cdsKey}">
<td th:text="${cnt.count}"></td> <td th:text="${searchParams.contentCnt-(20*(searchParams.pageIndex-1))-cnt.index}"></td>
<td th:text="${#temporals.format(dto.napoDt, 'yyyy-MM-dd HH:mm')}"></td> <td th:text="${cdi.caseNum}"></td>
<td th:text="${cdi.mmsi}"></td>
<td th:text="${#temporals.format(cdi.napoDt, 'yyyy-MM-dd HH:mm')}"></td>
<td> <td>
<div th:if="${!#strings.isEmpty(dto.napoSeaPointLon) and !#strings.isEmpty(dto.napoSeaPointLon)}" th:text="${#strings.concat(dto.napoSeaPointLon, ' ~ ', dto.napoSeaPointLat)}"></div> <div th:if="${!#strings.isEmpty(cdi.napoSeaPointLon) and !#strings.isEmpty(cdi.napoSeaPointLon)}" th:text="${#strings.concat(cdi.napoSeaPointLon, ' ~ ', cdi.napoSeaPointLat)}"></div>
<div th:if="${!#strings.isEmpty(dto.napoSeaPointLon) or !#strings.isEmpty(dto.napoSeaPointLon)}" th:text="${#strings.concat(dto.napoSeaPointLon, dto.napoSeaPointLat)}"></div> <div th:if="${!#strings.isEmpty(cdi.napoSeaPointLon) or !#strings.isEmpty(cdi.napoSeaPointLon)}" th:text="${#strings.concat(cdi.napoSeaPointLon, cdi.napoSeaPointLat)}"></div>
<div th:text="${dto.napoSeaPointDetail}"></div> <div th:text="${cdi.napoSeaPointDetail}"></div>
</td> </td>
<td>
<th:block th:each="code:${session.commonCode.get('OG')}">
<th:block th:if="${code.itemCd eq cdi.caseAgency}" th:text="${code.itemValue}"></th:block>
</th:block>
</td>
<td th:text="${cdi.casePoliceOfficer}"></td>
<td>
<th:block th:each="code:${session.commonCode.get('CPO')}">
<th:block th:if="${code.itemCd eq cdi.crackdownPolice}" th:text="${code.itemValue}"></th:block>
</th:block>
</td>
<td>
<th:block th:each="code:${session.commonCode.get(cdi.crackdownPolice)}">
<th:block th:if="${code.itemCd eq cdi.crackdownBoat}" th:text="${code.itemValue}"></th:block>
</th:block>
</td>
<td>
<th:block th:each="code:${session.commonCode.get('IST')}">
<th:block th:if="${code.itemCd eq cdi.invasionType}" th:text="${code.itemValue}"></th:block>
</th:block>
</td>
<td th:text="${cdi.nll eq 'Y'?'O':'X'}"></td>
<td th:text="${cdi.fieldIvsgt eq 'C'?'압송':'현장조사'}"></td>
<td>
<th:block th:each="code:${session.commonCode.get('JT')}">
<th:block th:if="${code.itemCd eq cdi.wrtUserGrd}" th:text="${code.itemValue}"></th:block>
</th:block>
<th:block th:text="${cdi.wrtUserNm}"></th:block>
</td>
<td th:text="${#temporals.format(cdi.wrtDt, 'yyyy-MM-dd HH:mm')}"></td>
</tr> </tr>
</th:block> </th:block>
</tbody> </tbody>