|
|
@ -17,25 +17,65 @@ |
|
|
|
|
|
|
|
package com.elink.esua.epdc.service.impl; |
|
|
|
|
|
|
|
import cn.afterturn.easypoi.excel.ExcelExportUtil; |
|
|
|
import cn.afterturn.easypoi.excel.ExcelImportUtil; |
|
|
|
import cn.afterturn.easypoi.excel.entity.ImportParams; |
|
|
|
import cn.hutool.core.collection.CollUtil; |
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
|
|
|
import com.baomidou.mybatisplus.core.metadata.IPage; |
|
|
|
import com.elink.esua.epdc.commons.mybatis.service.impl.BaseServiceImpl; |
|
|
|
import com.elink.esua.epdc.commons.tools.constant.NumConstant; |
|
|
|
import com.elink.esua.epdc.commons.tools.constant.OrganizationTypeConstant; |
|
|
|
import com.elink.esua.epdc.commons.tools.constant.StrConstant; |
|
|
|
import com.elink.esua.epdc.commons.tools.enums.YesOrNoEnum; |
|
|
|
import com.elink.esua.epdc.commons.tools.exception.RenException; |
|
|
|
import com.elink.esua.epdc.commons.tools.page.PageData; |
|
|
|
import com.elink.esua.epdc.commons.tools.security.user.SecurityUser; |
|
|
|
import com.elink.esua.epdc.commons.tools.security.user.UserDetail; |
|
|
|
import com.elink.esua.epdc.commons.tools.utils.ConvertUtils; |
|
|
|
import com.elink.esua.epdc.commons.tools.constant.FieldConstant; |
|
|
|
import com.elink.esua.epdc.commons.tools.utils.LocalDateUtils; |
|
|
|
import com.elink.esua.epdc.commons.tools.utils.Result; |
|
|
|
import com.elink.esua.epdc.commons.tools.xss.XssHttpServletRequestWrapper; |
|
|
|
import com.elink.esua.epdc.constant.WorkAppMenuConstant; |
|
|
|
import com.elink.esua.epdc.dao.ManualScoreDao; |
|
|
|
import com.elink.esua.epdc.dto.DeptLevelAndLeaderDTO; |
|
|
|
import com.elink.esua.epdc.dto.ManualScoreDTO; |
|
|
|
import com.elink.esua.epdc.dto.epdc.form.UserSysDeptInfoFormDTO; |
|
|
|
import com.elink.esua.epdc.dto.epdc.result.UserSysDeptInfoResultDTO; |
|
|
|
import com.elink.esua.epdc.dto.result.KpiManualScoreResultDTO; |
|
|
|
import com.elink.esua.epdc.dto.result.KpiManualScoreTemplateResultDTO; |
|
|
|
import com.elink.esua.epdc.entity.KpiManualScoreEntity; |
|
|
|
import com.elink.esua.epdc.entity.KpiRuleEntity; |
|
|
|
import com.elink.esua.epdc.entity.ManualScoreEntity; |
|
|
|
import com.elink.esua.epdc.enums.KpiCycleEnum; |
|
|
|
import com.elink.esua.epdc.excel.KpiManualScoreTemplateExcel; |
|
|
|
import com.elink.esua.epdc.excel.ManualScoreExcel; |
|
|
|
import com.elink.esua.epdc.feign.AdminFeignClient; |
|
|
|
import com.elink.esua.epdc.redis.ManualScoreRedis; |
|
|
|
import com.elink.esua.epdc.service.ExplicitConstraint; |
|
|
|
import com.elink.esua.epdc.service.ManualScoreService; |
|
|
|
import com.elink.esua.epdc.utils.DeptUtils; |
|
|
|
import com.elink.esua.epdc.utils.StreamUtils; |
|
|
|
import com.google.common.collect.Lists; |
|
|
|
import org.apache.commons.lang3.StringUtils; |
|
|
|
import org.apache.poi.hssf.usermodel.*; |
|
|
|
import org.apache.poi.ss.usermodel.DataValidationHelper; |
|
|
|
import org.apache.poi.ss.usermodel.Sheet; |
|
|
|
import org.apache.poi.ss.usermodel.Workbook; |
|
|
|
import org.apache.poi.ss.util.CellRangeAddressList; |
|
|
|
import org.apache.poi.xssf.usermodel.*; |
|
|
|
import org.springframework.beans.factory.annotation.Autowired; |
|
|
|
import org.springframework.stereotype.Service; |
|
|
|
import org.springframework.transaction.annotation.Transactional; |
|
|
|
import org.springframework.web.multipart.MultipartFile; |
|
|
|
|
|
|
|
import java.util.Arrays; |
|
|
|
import java.util.List; |
|
|
|
import java.util.Map; |
|
|
|
import java.io.File; |
|
|
|
import java.lang.reflect.Field; |
|
|
|
import java.math.BigDecimal; |
|
|
|
import java.time.LocalDate; |
|
|
|
import java.time.YearMonth; |
|
|
|
import java.util.*; |
|
|
|
|
|
|
|
/** |
|
|
|
* 手动打分表 |
|
|
@ -49,6 +89,12 @@ public class ManualScoreServiceImpl extends BaseServiceImpl<ManualScoreDao, Manu |
|
|
|
@Autowired |
|
|
|
private ManualScoreRedis manualScoreRedis; |
|
|
|
|
|
|
|
@Autowired |
|
|
|
private AdminFeignClient adminFeignClient; |
|
|
|
|
|
|
|
@Autowired |
|
|
|
private DeptUtils deptUtils; |
|
|
|
|
|
|
|
@Override |
|
|
|
public PageData<ManualScoreDTO> page(Map<String, Object> params) { |
|
|
|
IPage<ManualScoreEntity> page = baseDao.selectPage( |
|
|
@ -83,13 +129,58 @@ public class ManualScoreServiceImpl extends BaseServiceImpl<ManualScoreDao, Manu |
|
|
|
@Override |
|
|
|
@Transactional(rollbackFor = Exception.class) |
|
|
|
public void save(ManualScoreDTO dto) { |
|
|
|
UserDetail user = SecurityUser.getUser(); |
|
|
|
// 考核周期开始月
|
|
|
|
YearMonth scoreStartDateMonth = YearMonth.parse(dto.getMonthString()); |
|
|
|
dto.setMonth(LocalDateUtils.localDateToDate(scoreStartDateMonth.atDay(NumConstant.ONE))); |
|
|
|
switch (dto.getManualScore()) { |
|
|
|
case "优": |
|
|
|
dto.setScore(new BigDecimal(100)); |
|
|
|
break; |
|
|
|
case "良": |
|
|
|
dto.setScore(new BigDecimal(80)); |
|
|
|
break; |
|
|
|
case "中": |
|
|
|
dto.setScore(new BigDecimal(60)); |
|
|
|
break; |
|
|
|
case "差": |
|
|
|
dto.setScore(new BigDecimal(40)); |
|
|
|
break; |
|
|
|
default: |
|
|
|
break; |
|
|
|
} |
|
|
|
ManualScoreEntity entity = ConvertUtils.sourceToTarget(dto, ManualScoreEntity.class); |
|
|
|
|
|
|
|
DeptLevelAndLeaderDTO deptLevelInfo = deptUtils.getDeptLevelInfo(entity.getDeptId(), YesOrNoEnum.YES); |
|
|
|
entity.setDeptId(deptLevelInfo.getDeptId()); |
|
|
|
entity.setAllDeptIds(deptLevelInfo.getAllDeptIds()); |
|
|
|
entity.setAllDeptNames(deptLevelInfo.getAllDeptNames()); |
|
|
|
entity.setParentDeptIds(deptLevelInfo.getParentDeptIds()); |
|
|
|
entity.setParentDeptNames(deptLevelInfo.getParentDeptNames()); |
|
|
|
entity.setCreatorDeptId(user.getDeptId()); |
|
|
|
entity.setCreatorDeptName(user.getDeptName()); |
|
|
|
insert(entity); |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
@Transactional(rollbackFor = Exception.class) |
|
|
|
public void update(ManualScoreDTO dto) { |
|
|
|
switch (dto.getManualScore()) { |
|
|
|
case "优": |
|
|
|
dto.setScore(new BigDecimal(100)); |
|
|
|
break; |
|
|
|
case "良": |
|
|
|
dto.setScore(new BigDecimal(80)); |
|
|
|
break; |
|
|
|
case "中": |
|
|
|
dto.setScore(new BigDecimal(60)); |
|
|
|
break; |
|
|
|
case "差": |
|
|
|
dto.setScore(new BigDecimal(40)); |
|
|
|
break; |
|
|
|
default: |
|
|
|
break; |
|
|
|
} |
|
|
|
ManualScoreEntity entity = ConvertUtils.sourceToTarget(dto, ManualScoreEntity.class); |
|
|
|
updateById(entity); |
|
|
|
} |
|
|
@ -101,4 +192,271 @@ public class ManualScoreServiceImpl extends BaseServiceImpl<ManualScoreDao, Manu |
|
|
|
baseDao.deleteBatchIds(Arrays.asList(ids)); |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public List<ManualScoreDTO> listManualScoreExportTemplate(Map<String, Object> params) { |
|
|
|
//用户所选被打分的机构类型
|
|
|
|
String deptType = params.get("deptType").toString(); |
|
|
|
// yyyy-MM
|
|
|
|
String month = params.get("month").toString(); |
|
|
|
// 考核周期开始月
|
|
|
|
YearMonth scoreStartDateMonth = YearMonth.parse(month); |
|
|
|
|
|
|
|
// 用户拥有的所有部门权限
|
|
|
|
List<Long> dataScopeDeptList = SecurityUser.getUser().getDeptIdList(); |
|
|
|
Result<List<UserSysDeptInfoResultDTO>> result = queryUserSysDeptInfo(dataScopeDeptList, deptType); |
|
|
|
|
|
|
|
if (!result.success() || CollUtil.isEmpty(result.getData())) { |
|
|
|
return Lists.newArrayList(); |
|
|
|
} |
|
|
|
List<ManualScoreDTO> list = Lists.newArrayList(); |
|
|
|
|
|
|
|
List<UserSysDeptInfoResultDTO> userSysDeptInfoResultDTOList = result.getData(); |
|
|
|
for (UserSysDeptInfoResultDTO userSysDeptInfoResultDTO : userSysDeptInfoResultDTOList) { |
|
|
|
|
|
|
|
ManualScoreDTO manualScoreDTO = new ManualScoreDTO(); |
|
|
|
|
|
|
|
if (OrganizationTypeConstant.ORG_TYPE_STREET_DEPT.equals(params.get("deptType").toString())){ |
|
|
|
deptType = "街道"; |
|
|
|
manualScoreDTO.setDeptName(userSysDeptInfoResultDTO.getStreet()); |
|
|
|
} else if (OrganizationTypeConstant.ORG_TYPE_COMMUNITY_PARTY.equals(params.get("deptType").toString())){ |
|
|
|
deptType = "社区"; |
|
|
|
manualScoreDTO.setDeptName(userSysDeptInfoResultDTO.getStreet() + "-" + |
|
|
|
userSysDeptInfoResultDTO.getName()); |
|
|
|
} else if (OrganizationTypeConstant.ORG_TYPE_GRID_PARTY.equals(params.get("deptType").toString())){ |
|
|
|
deptType = "网格"; |
|
|
|
manualScoreDTO.setDeptName(userSysDeptInfoResultDTO.getStreet() + "-" + |
|
|
|
userSysDeptInfoResultDTO.getCommunity() + "-" + userSysDeptInfoResultDTO.getGrid()); |
|
|
|
} |
|
|
|
|
|
|
|
manualScoreDTO.setDeptType(deptType); |
|
|
|
if ("0".equals(params.get("scoreType").toString())){ |
|
|
|
manualScoreDTO.setScoreType("月"); |
|
|
|
} else { |
|
|
|
manualScoreDTO.setScoreType("年"); |
|
|
|
} |
|
|
|
|
|
|
|
manualScoreDTO.setScoreStartDate(LocalDateUtils.localDateToDate(scoreStartDateMonth.atDay(NumConstant.ONE))); |
|
|
|
|
|
|
|
manualScoreDTO.setScore(null); |
|
|
|
list.add(manualScoreDTO); |
|
|
|
} |
|
|
|
|
|
|
|
return list; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* firstRow 開始行號 根据此项目,默认为2(下标0开始) |
|
|
|
* lastRow 根据此项目,默认为最大65535 |
|
|
|
* firstCol 区域中第一个单元格的列号 (下标0开始) |
|
|
|
* lastCol 区域中最后一个单元格的列号 |
|
|
|
* strings 下拉内容 |
|
|
|
* */ |
|
|
|
public static void selectList(Workbook workbook, int firstCol, int lastCol, String[] strings ){ |
|
|
|
Sheet sheet = workbook.getSheetAt(0); |
|
|
|
// 生成下拉列表
|
|
|
|
// 只对(x,x)单元格有效
|
|
|
|
CellRangeAddressList cellRangeAddressList = new CellRangeAddressList(2, 65535, firstCol, lastCol); |
|
|
|
// 生成下拉框内容
|
|
|
|
DVConstraint dvConstraint = DVConstraint.createExplicitListConstraint(strings); |
|
|
|
HSSFDataValidation dataValidation = new HSSFDataValidation(cellRangeAddressList, dvConstraint); |
|
|
|
// 对sheet页生效
|
|
|
|
sheet.addValidationData(dataValidation); |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public Result importManualScoreExcel(MultipartFile file) { |
|
|
|
File f = StreamUtils.conversionFile(file); |
|
|
|
try { |
|
|
|
//判断上传文件类型
|
|
|
|
String originalFilename = file.getOriginalFilename(); |
|
|
|
if (!originalFilename.endsWith(StrConstant.EXCEL_SUFFIX_2003) && !originalFilename.endsWith(StrConstant.EXCEL_SUFFIX_2007)) { |
|
|
|
return new Result().error("请选择.xls或者.xlsx格式文件"); |
|
|
|
} |
|
|
|
List<ManualScoreExcel> recordList = ExcelImportUtil.importExcel(f, ManualScoreExcel.class, new ImportParams()); |
|
|
|
if (CollUtil.isEmpty(recordList)) { |
|
|
|
return new Result().error("导入内容不能为空"); |
|
|
|
} |
|
|
|
UserDetail user = SecurityUser.getUser(); |
|
|
|
// 校验excel数据
|
|
|
|
// List<ExcelError> checkResult = checkExcelForManualScore(recordList, user.getDeptIdList());
|
|
|
|
// if (CollUtil.isNotEmpty(checkResult)) {
|
|
|
|
// Result result = new Result().error();
|
|
|
|
// result.setData(checkResult);
|
|
|
|
// return result;
|
|
|
|
// }
|
|
|
|
for (ManualScoreExcel manualScoreExcel : recordList) { |
|
|
|
ManualScoreEntity manualScoreEntity = packageManualScoreByExcelData(manualScoreExcel, user); |
|
|
|
baseDao.insert(manualScoreEntity); |
|
|
|
} |
|
|
|
} catch (Exception e) { |
|
|
|
return new Result().error("数据异常,解析excel文件失败"); |
|
|
|
} finally { |
|
|
|
// 删除文件
|
|
|
|
this.deleteAllFilesOfDir(f); |
|
|
|
} |
|
|
|
return new Result(); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* @Description: 调用admin模块查询部门信息详情 |
|
|
|
* @param dataScopeDeptList 用户数据权限 |
|
|
|
* @param deptTypeKey 部门类别 |
|
|
|
* @return: com.elink.esua.epdc.commons.tools.utils.Result<java.util.List<com.elink.esua.epdc.dto.epdc.result.UserSysDeptInfoResultDTO>> |
|
|
|
* @Author: zy |
|
|
|
* @Date: 2020-04-14 |
|
|
|
*/ |
|
|
|
private Result<List<UserSysDeptInfoResultDTO>> queryUserSysDeptInfo(List<Long> dataScopeDeptList, String deptTypeKey) { |
|
|
|
|
|
|
|
UserSysDeptInfoFormDTO userSysDeptInfoFormDTO = new UserSysDeptInfoFormDTO(); |
|
|
|
|
|
|
|
List<String> typeKeyList = new ArrayList<>(); |
|
|
|
typeKeyList.add(deptTypeKey); |
|
|
|
userSysDeptInfoFormDTO.setTypeKeyList(typeKeyList); |
|
|
|
|
|
|
|
userSysDeptInfoFormDTO.setDeptIdList(dataScopeDeptList); |
|
|
|
|
|
|
|
return adminFeignClient.queryUserSysDeptInfo(userSysDeptInfoFormDTO); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* @Description: 组装手动打分实体 |
|
|
|
* @Param: [excelData excel中的一条数据 |
|
|
|
* @Param: user] 当前用户 |
|
|
|
* @return: com.elink.esua.epdc.entity.ManualScoreEntity |
|
|
|
* @Author: zy |
|
|
|
* @Date: 2020-04-14 |
|
|
|
*/ |
|
|
|
private ManualScoreEntity packageManualScoreByExcelData(ManualScoreExcel excelData, UserDetail user) { |
|
|
|
|
|
|
|
// 获取用户所有数据权限对应的部门
|
|
|
|
List<DeptLevelAndLeaderDTO> deptList = getDeptListByDateScope(user.getDeptIdList()); |
|
|
|
DeptLevelAndLeaderDTO deptByExcelData = getDeptByExcelData(excelData, deptList); |
|
|
|
if (null == deptByExcelData) { |
|
|
|
throw new RenException("匹配数据权限失败"); |
|
|
|
} |
|
|
|
|
|
|
|
DeptLevelAndLeaderDTO deptLevelInfo = deptUtils.getDeptLevelInfo(deptByExcelData.getDeptId(), YesOrNoEnum.YES); |
|
|
|
ManualScoreEntity manualScoreEntity = new ManualScoreEntity(); |
|
|
|
manualScoreEntity.setDeptId(deptLevelInfo.getDeptId()); |
|
|
|
manualScoreEntity.setAllDeptIds(deptLevelInfo.getAllDeptIds()); |
|
|
|
manualScoreEntity.setAllDeptNames(deptLevelInfo.getAllDeptNames()); |
|
|
|
manualScoreEntity.setParentDeptIds(deptLevelInfo.getParentDeptIds()); |
|
|
|
manualScoreEntity.setParentDeptNames(deptLevelInfo.getParentDeptNames()); |
|
|
|
manualScoreEntity.setCreatorDeptId(user.getDeptId()); |
|
|
|
manualScoreEntity.setCreatorDeptName(user.getDeptName()); |
|
|
|
switch (excelData.getScore()) { |
|
|
|
case "优": |
|
|
|
manualScoreEntity.setScore(new BigDecimal(100)); |
|
|
|
break; |
|
|
|
case "良": |
|
|
|
manualScoreEntity.setScore(new BigDecimal(80)); |
|
|
|
break; |
|
|
|
case "中": |
|
|
|
manualScoreEntity.setScore(new BigDecimal(60)); |
|
|
|
break; |
|
|
|
case "差": |
|
|
|
manualScoreEntity.setScore(new BigDecimal(40)); |
|
|
|
break; |
|
|
|
default: |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
//后期要进一步处理的字段值
|
|
|
|
manualScoreEntity.setMonth(excelData.getScoreStartDate()); |
|
|
|
manualScoreEntity.setScoreType(excelData.getScoreType()); |
|
|
|
return manualScoreEntity; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* @Description: 通过数据权限获取所有部门列表 |
|
|
|
* @Param: [deptIdList] |
|
|
|
* @return: java.util.List<com.elink.esua.epdc.dto.DeptLevelAndLeaderDTO> |
|
|
|
* @Author: zy |
|
|
|
* @Date: 2020-04-14 |
|
|
|
*/ |
|
|
|
private List<DeptLevelAndLeaderDTO> getDeptListByDateScope(List<Long> deptIdList) { |
|
|
|
List<DeptLevelAndLeaderDTO> deptList = Lists.newArrayList(); |
|
|
|
DeptLevelAndLeaderDTO deptLevelInfo; |
|
|
|
for (Long deptId : deptIdList) { |
|
|
|
deptLevelInfo = deptUtils.getDeptLevelInfo(deptId, YesOrNoEnum.NO); |
|
|
|
deptList.add(deptLevelInfo); |
|
|
|
} |
|
|
|
return deptList; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* @Description: 从数据权限的所有部门中,匹配excel中的一行数据 |
|
|
|
* @param excelData excel中的某一行 |
|
|
|
* @param deptList 用户部门权限中的所有部门 |
|
|
|
* @return: com.elink.esua.epdc.dto.DeptLevelAndLeaderDTO |
|
|
|
* @Author: zy |
|
|
|
* @Date: 2020-04-14 |
|
|
|
*/ |
|
|
|
private DeptLevelAndLeaderDTO getDeptByExcelData(ManualScoreExcel excelData, List<DeptLevelAndLeaderDTO> deptList) { |
|
|
|
String[] deptNameAll = excelData.getDeptName().split("-"); |
|
|
|
String allDeptNames = ""; |
|
|
|
if (deptNameAll.length == 3) { |
|
|
|
allDeptNames = deptNameAll[0].trim() |
|
|
|
.concat(StrConstant.HYPHEN).concat(deptNameAll[1].trim()) |
|
|
|
.concat(StrConstant.HYPHEN).concat(deptNameAll[2].trim()); |
|
|
|
} else if (deptNameAll.length == 2) { |
|
|
|
allDeptNames = deptNameAll[0].trim() |
|
|
|
.concat(StrConstant.HYPHEN).concat(deptNameAll[1].trim()); |
|
|
|
} else if (deptNameAll.length == 1) { |
|
|
|
allDeptNames = deptNameAll[0].trim(); |
|
|
|
} |
|
|
|
for (DeptLevelAndLeaderDTO oneDept : deptList) { |
|
|
|
if (oneDept.getAllDeptNames().contains(allDeptNames)) { |
|
|
|
return oneDept; |
|
|
|
} |
|
|
|
} |
|
|
|
return null; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 删除文件夹(强制删除) |
|
|
|
* |
|
|
|
* @param path |
|
|
|
*/ |
|
|
|
private void deleteAllFilesOfDir(File path) { |
|
|
|
if (null != path) { |
|
|
|
if (!path.exists()) { |
|
|
|
return; |
|
|
|
} |
|
|
|
if (path.isFile()) { |
|
|
|
boolean result = path.delete(); |
|
|
|
int tryCount = 0; |
|
|
|
while (!result && tryCount++ < 10) { |
|
|
|
System.gc(); // 回收资源
|
|
|
|
result = path.delete(); |
|
|
|
} |
|
|
|
} |
|
|
|
File[] files = path.listFiles(); |
|
|
|
if (null != files) { |
|
|
|
for (int i = 0; i < files.length; i++) { |
|
|
|
deleteAllFilesOfDir(files[i]); |
|
|
|
} |
|
|
|
} |
|
|
|
path.delete(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public PageData<ManualScoreDTO> listManualScoreResultDTO(Map<String, Object> params) { |
|
|
|
String allDeptIds = ""; |
|
|
|
if (params.get("streetId") != "" && params.get("streetId") != null) { |
|
|
|
allDeptIds = params.get("streetId").toString(); //查询条件 街道
|
|
|
|
} |
|
|
|
if (params.get("communityId") != "" && params.get("communityId") != null) { |
|
|
|
allDeptIds = params.get("communityId").toString(); //查询条件 社区
|
|
|
|
} |
|
|
|
if (params.get("gridId") != "" && params.get("gridId") != null) { |
|
|
|
allDeptIds = params.get("gridId").toString(); //查询条件 网格
|
|
|
|
} |
|
|
|
params.put("allDeptIds", allDeptIds); |
|
|
|
IPage<ManualScoreDTO> page = getPage(params); |
|
|
|
List<ManualScoreDTO> list = baseDao.selectListManualScoreResultDTO(params); |
|
|
|
return new PageData<>(list, page.getTotal()); |
|
|
|
} |
|
|
|
|
|
|
|
} |