Преглед изворни кода

feat: 实现车辆过检数据导入接口

weijianghai пре 9 месеци
родитељ
комит
06d183b668

+ 17 - 0
src/main/java/com/nokia/financeapi/dao/car/CarGuoJianDao.java

@@ -0,0 +1,17 @@
+package com.nokia.financeapi.dao.car;
+
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+
+@Mapper
+public interface CarGuoJianDao {
+    /**
+     * 判断是否有数据
+     * @param endYearMonth 账期
+     */
+    @Select("""
+select exists (select 1 from car.car_guo_jian where year_month = #{endYearMonth})
+""")
+    boolean hasData(@Param("endYearMonth") Integer endYearMonth);
+}

+ 48 - 0
src/main/java/com/nokia/financeapi/dao/gdc/GdcCarGuoJianDao.java

@@ -0,0 +1,48 @@
+package com.nokia.financeapi.dao.gdc;
+
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Update;
+
+@Mapper
+public interface GdcCarGuoJianDao {
+    /**
+     * 插入过检数据
+     * @param endYearMonth 账期
+     */
+    @Update("""
+insert
+    into
+    car_theme.wz_f_un_annual_inspectionleased_vehicles_details
+(
+statistical_month,
+    card_num,
+    car_brand,
+    city,
+    dpt_sec,
+    grid,
+    enable_date,
+    passed_inspection,
+    should_inspection_be_conducted_this_month,
+    annual_inspection_this_month,
+    inspect_annually_date
+)
+select
+    year_month as statistical_month,
+    che_pai_hao as card_num,
+    che_xing as car_brand,
+    first_unit as city,
+    second_unit as dpt_sec,
+    third_unit as grid,
+    deng_ji_ri_qi as enable_date,
+    shi_fou_guo_jian as passed_inspection,
+    shi_fou_ben_yue_ying_jian as should_inspection_be_conducted_this_month,
+    ben_yue_shi_fou_nian_jian as annual_inspection_this_month,
+    nian_jian_shi_jian as inspect_annually_date
+from
+    car.car_guo_jian
+where
+    year_month = #{endYearMonth}
+""")
+    int insertChaoBao(@Param("endYearMonth") Integer endYearMonth);
+}

+ 1 - 1
src/main/java/com/nokia/financeapi/service/car/CarChaoBaoImportService.java

@@ -268,7 +268,7 @@ public class CarChaoBaoImportService {
             String chaoBaoTianShu = map.get("chao_bao_tian_shu");
             if (StringUtils.hasText(chaoBaoTianShu)) {
                 try {
-                    map.put("chao_bao_tian_shu", new BigDecimal(chaoBaoTianShu).toString());
+                    map.put("chao_bao_tian_shu", String.valueOf(new BigDecimal(chaoBaoTianShu).intValue()));
                 } catch (Exception e) {
                     map.put("chao_bao_tian_shu", "");
                     log.warn("{}超出建议保养时间(天)解析失败: {}", rawChePaiHao, chaoBaoTianShu);

+ 6 - 1
src/main/java/com/nokia/financeapi/service/car/CarDataImportService.java

@@ -14,11 +14,14 @@ import org.springframework.web.multipart.MultipartFile;
 public class CarDataImportService {
     private final CarWeiZhangImportService carWeiZhangImportService;
     private final CarChaoBaoImportService carChaoBaoImportService;
+    private final CarGuoJianImportService carGuoJianImportService;
 
     public CarDataImportService(CarWeiZhangImportService carWeiZhangImportService,
-                                CarChaoBaoImportService carChaoBaoImportService) {
+                                CarChaoBaoImportService carChaoBaoImportService,
+                                CarGuoJianImportService carGuoJianImportService) {
         this.carWeiZhangImportService = carWeiZhangImportService;
         this.carChaoBaoImportService = carChaoBaoImportService;
+        this.carGuoJianImportService = carGuoJianImportService;
     }
 
     public R<Object> dataImport(CarDataImportDto dto, MultipartFile file) {
@@ -33,6 +36,8 @@ public class CarDataImportService {
                 carWeiZhangImportService.dataImport(dto, file);
             } else if (CarDataImportEnum.CHAO_BAO.equals(dto.getId())) {
                 carChaoBaoImportService.dataImport(dto, file);
+            } else if (CarDataImportEnum.GUO_JIAN.equals(dto.getId())) {
+                carGuoJianImportService.dataImport(dto, file);
             }
             return R.ok();
         } catch (BizException e) {

+ 300 - 0
src/main/java/com/nokia/financeapi/service/car/CarGuoJianImportService.java

@@ -0,0 +1,300 @@
+package com.nokia.financeapi.service.car;
+
+import com.nokia.financeapi.common.exception.BizException;
+import com.nokia.financeapi.common.exception.MyRuntimeException;
+import com.nokia.financeapi.common.utils.psql.PsqlUtil;
+import com.nokia.financeapi.config.CarDataImportConfig;
+import com.nokia.financeapi.dao.car.CarGuoJianDao;
+import com.nokia.financeapi.dao.gdc.GdcCarGuoJianDao;
+import com.nokia.financeapi.pojo.dto.CarDataImportDto;
+import com.nokia.financeapi.pojo.po.common.AreaPo;
+import com.nokia.financeapi.pojo.po.common.OrganizationPo;
+import com.nokia.financeapi.service.common.AreaService;
+import com.nokia.financeapi.service.common.OrganizationService;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.csv.CSVFormat;
+import org.apache.commons.csv.CSVPrinter;
+import org.apache.commons.io.FileUtils;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.DateUtil;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStreamWriter;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Stream;
+
+@Slf4j
+@Service
+public class CarGuoJianImportService {
+    private final CarDataImportConfig carDataImportConfig;
+    private final CarService carService;
+    private final OrganizationService organizationService;
+    private final AreaService areaService;
+    private final CarGuoJianDao carGuoJianDao;
+    private final GdcCarGuoJianDao gdcCarGuoJianDao;
+
+    public CarGuoJianImportService(CarDataImportConfig carDataImportConfig, CarService carService,
+                                   OrganizationService organizationService, AreaService areaService,
+                                   CarGuoJianDao carGuoJianDao, GdcCarGuoJianDao gdcCarGuoJianDao) {
+        this.carDataImportConfig = carDataImportConfig;
+        this.carService = carService;
+        this.organizationService = organizationService;
+        this.areaService = areaService;
+        this.carGuoJianDao = carGuoJianDao;
+        this.gdcCarGuoJianDao = gdcCarGuoJianDao;
+    }
+
+    @Transactional(timeout = 120, rollbackFor = Exception.class)
+    public synchronized void dataImport(CarDataImportDto dto, MultipartFile file) throws IOException {
+        boolean flag = carGuoJianDao.hasData(dto.getYearMonth());
+        if (flag) {
+            throw new BizException("该账期数据已存在");
+        }
+        Files.createDirectories(Paths.get(carDataImportConfig.getGuoJian()));
+        File fileNew = new File(carDataImportConfig.getGuoJian() + dto.getYearMonth() + ".xlsx");
+        FileUtils.copyInputStreamToFile(file.getInputStream(), fileNew);
+        Path path = fileNew.toPath();
+        List<Map<String, String>> list = readFile(path);
+        List<Map<String, String>> distinctList = dataProcessing(path, list, dto);
+        Path csvPath = toCsv(path, distinctList);
+        copyCsv(csvPath);
+        procedure(dto);
+    }
+
+    /**
+     * 读取文件
+     *
+     * @param path 文件路径
+     */
+    public List<Map<String, String>> readFile(Path path) throws IOException {
+        log.info("读取: {}", path);
+        List<String> rawHeaders = Stream.of("账期", "车牌号", "车型", "单位", "二级单位", "三级单位", "登记日期",
+                        "年检时间", "是否过检", "是否本月应检", "本月是否年检").toList();
+        List<String> headers = Stream.of("year_month", "che_pai_hao", "che_xing", "raw_yi_ji", "raw_er_ji",
+                "raw_san_ji", "deng_ji_ri_qi", "nian_jian_shi_jian", "shi_fou_guo_jian", "shi_fou_ben_yue_ying_jian",
+                "ben_yue_shi_fou_nian_jian").toList();
+        try (InputStream inputStream = Files.newInputStream(path);
+             Workbook workbook = new XSSFWorkbook(inputStream)
+        ) {
+            List<Map<String, String>> resultList = new ArrayList<>();
+            // 读取第一个工作表
+            Sheet sheet = workbook.getSheetAt(0);
+            // 表头行
+            Row headerRow = sheet.getRow(0);
+            // 列数
+            int columnCount = headerRow.getPhysicalNumberOfCells();
+            log.info("columnCount: {}", columnCount);
+            // 检查表头
+            if (headers.size() != columnCount) {
+                throw new BizException("列数错误");
+            }
+            for (int i = 0; i < columnCount; i++) {
+                Cell cell = headerRow.getCell(i);
+                if (cell == null || !rawHeaders.get(i).equals(cell.getStringCellValue())) {
+                    throw new BizException("表头错误");
+                }
+            }
+            // 最后行数
+            int lastRowNum = sheet.getLastRowNum();
+            log.info("lastRowNum: {}", lastRowNum);
+            if (lastRowNum == 0) {
+                throw new BizException("文件为空");
+            }
+            // 遍历行
+            for (int i = 1; i <= lastRowNum; i++) {
+                Row row = sheet.getRow(i);
+                if (row == null) {
+                    continue;
+                }
+                Map<String, String> rowMap = new LinkedHashMap<>();
+                // 遍历列
+                for (int j = 0; j < columnCount; j++) {
+                    String header = headers.get(j);
+                    String cellValue = "";
+                    rowMap.put(header, cellValue);
+                    Cell cell = row.getCell(j);
+                    if (cell == null) {
+                        continue;
+                    }
+                    switch (cell.getCellType()) {
+                        case STRING:
+                            boolean skipTrim = "deng_ji_ri_qi".equals(header) || "nian_jian_shi_jian".equals(header);
+                            // 删除字符串空白字符
+                            cellValue = skipTrim ? cell.getStringCellValue()
+                                    : StringUtils.trimAllWhitespace(cell.getStringCellValue());
+                            break;
+                        case NUMERIC:
+                            if (DateUtil.isCellDateFormatted(cell)) {
+                                cellValue = DateUtil.getLocalDateTime(cell.getNumericCellValue())
+                                        .format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
+                                break;
+                            }
+                            cellValue = String.valueOf(cell.getNumericCellValue());
+                            break;
+                        case BOOLEAN:
+                            cellValue = String.valueOf(cell.getBooleanCellValue());
+                            break;
+                        default:
+                            break;
+                    }
+                    rowMap.put(header, cellValue);
+                }
+                resultList.add(rowMap);
+            }
+            return resultList;
+        }
+    }
+
+    /**
+     * 数据加工
+     *
+     * @param path 文件路径
+     * @param list 数据
+     * @param dto 请求参数
+     */
+    public List<Map<String, String>> dataProcessing(Path path, List<Map<String, String>> list, CarDataImportDto dto) {
+        String yearMonth = dto.getYearMonth().toString();
+        LocalDate localDate = LocalDate.parse(yearMonth + "01", DateTimeFormatter.ofPattern("yyyyMMdd"));
+        String year = String.valueOf(localDate.getYear());
+        String month = String.valueOf(localDate.getMonthValue());
+        List<OrganizationPo> secondOrgs = organizationService.getSecondOrgs();
+        List<OrganizationPo> thirdOrgs = organizationService.getThirdOrgs();
+        Map<String, OrganizationPo> orgMap = organizationService.getOrgMap(secondOrgs, thirdOrgs);
+        Map<String, List<OrganizationPo>> thirdOrganizationListMap =
+                organizationService.getThirdOrganizationListMap(secondOrgs, thirdOrgs);
+        List<AreaPo> cities = areaService.getCities();
+        List<AreaPo> districts = areaService.getDistricts();
+        Map<String, AreaPo> areaMap = areaService.getAreaMap(cities, districts);
+        Map<String, List<AreaPo>> districtListMap = areaService.getDistrictListMap(cities, districts);
+        for (Map<String, String> map : list) {
+            map.put("year_month", yearMonth);
+            map.put("year_no", year);
+            map.put("month_no", month);
+            String rawChePaiHao = map.get("che_pai_hao");
+            map.put("raw_che_pai_hao", rawChePaiHao);
+            String chePaiHao = carService.getChePai(rawChePaiHao);
+            map.put("che_pai_hao", chePaiHao);
+            String chePaiFail = carService.chePaiFail(rawChePaiHao);
+            map.put("che_pai_fail", chePaiFail);
+            String yj = map.get("raw_yi_ji");
+            String ej = map.get("raw_er_ji");
+            String sj = map.get("raw_san_ji");
+            String firstUnit = carService.getFirstUnit(yj);
+            map.put("first_unit", firstUnit);
+            String secondUnit = carService.getSecondUnit(ej, firstUnit);
+            map.put("second_unit", secondUnit);
+            String thirdUnit = carService.getThirdUnit(sj, secondUnit);
+            map.put("third_unit", thirdUnit);
+            String areaNo = carService.getAreaNo(secondOrgs, yj);
+            map.put("area_no", areaNo);
+            String areaName = carService.getOrgName(orgMap, areaNo);
+            map.put("area_name", areaName);
+            String cityNo = carService.getCityNo(thirdOrganizationListMap, areaNo, areaName, ej);
+            map.put("city_no", cityNo);
+            String cityName = carService.getOrgName(orgMap, cityNo);
+            map.put("city_name", cityName);
+            String areaNo2 = carService.getAreaNo2(areaName, cityName);
+            map.put("area_no2", areaNo2);
+            String areaName2 = carService.getOrgName(orgMap, areaNo2);
+            map.put("area_name2", areaName2);
+            String cityId = carService.getCityId(cities, yj);
+            map.put("city_id", cityId);
+            String city = carService.getAreaName(areaMap, cityId);
+            map.put("city", city);
+            String districtId = carService.getDistrictId(districtListMap, cityId, cityName, ej);
+            map.put("district_id", districtId);
+            String district = carService.getAreaName(areaMap, districtId);
+            map.put("district", district);
+            map.put("source", path.getFileName().toString());
+            String dengJiRiQi = map.get("deng_ji_ri_qi");
+            if (StringUtils.hasText(dengJiRiQi)) {
+                try {
+                    map.put("deng_ji_ri_qi",
+                            LocalDate.parse(dengJiRiQi).format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
+                } catch (Exception e) {
+                    map.put("deng_ji_ri_qi", "");
+                    log.warn("{}登记日期解析失败: {}", rawChePaiHao, dengJiRiQi);
+                }
+            }
+            String nianJianShiJian = map.get("nian_jian_shi_jian");
+            if (StringUtils.hasText(nianJianShiJian)) {
+                try {
+                    map.put("nian_jian_shi_jian",
+                            LocalDate.parse(nianJianShiJian).format(DateTimeFormatter.ofPattern("yyyyMM")));
+                } catch (Exception e) {
+                    map.put("nian_jian_shi_jian", "");
+                    log.warn("{}年检时间解析失败: {}", rawChePaiHao, nianJianShiJian);
+                }
+            }
+        }
+        return list;
+    }
+
+    /**
+     * 生成csv
+     *
+     * @param path 源文件路径
+     * @param list 数据
+     */
+    public Path toCsv(Path path, List<Map<String, String>> list) throws IOException {
+        log.info("条数:{}", list.size());
+        Path csvPath = Paths.get(path + ".csv");
+        try (OutputStreamWriter osw = new OutputStreamWriter(Files.newOutputStream(csvPath),
+                StandardCharsets.UTF_8);
+             CSVPrinter printer = new CSVPrinter(osw, CSVFormat.DEFAULT)) {
+            // 添加bom头避免excel乱码
+            osw.write('\ufeff');
+            Map<String, String> header = list.get(0);
+            // 表头
+            printer.printRecord(header.keySet());
+            for (Map<String, String> map : list) {
+                printer.printRecord(map.values());
+            }
+        }
+        return csvPath;
+    }
+
+    /**
+     * 导入数据库
+     *
+     * @param path 文件路径
+     */
+    public void copyCsv(Path path) {
+        String dbTable = "car.car_guo_jian";
+        String csv = path.toString();
+        String columns = "(year_month,che_pai_hao,che_xing,raw_yi_ji,raw_er_ji,raw_san_ji,deng_ji_ri_qi,nian_jian_shi_jian,shi_fou_guo_jian,shi_fou_ben_yue_ying_jian,ben_yue_shi_fou_nian_jian,year_no,month_no,raw_che_pai_hao,che_pai_fail,first_unit,second_unit,third_unit,area_no,area_name,city_no,city_name,area_no2,area_name2,city_id,city,district_id,district,source)";
+        Long timeout = 60000L;
+        PsqlUtil.copyCsv(carDataImportConfig.getCopyScriptPath(), carDataImportConfig.getDbHost(),
+                carDataImportConfig.getDbPort(), carDataImportConfig.getDbUsername(), carDataImportConfig.getDbPassword(),
+                carDataImportConfig.getDbName(), dbTable, csv, columns, timeout, null);
+    }
+
+    /**
+     * 更新数据库
+     */
+    public void procedure(CarDataImportDto dto) {
+        int update1 = gdcCarGuoJianDao.insertChaoBao(dto.getYearMonth());
+        if (update1 == 0) {
+            throw new MyRuntimeException("插入car_theme.wz_f_un_annual_inspectionleased_vehicles_details失败");
+        }
+    }
+}

+ 5 - 5
src/main/resources/application-dev.yml

@@ -34,10 +34,10 @@ car:
     db-username: postgres
     db-password: Test!234
     db-name: financialdb
-    wei-zhang: data-import/car/wei-zhang/
-    chao-bao: data-import/car/chao-bao/
-    guo-jian: data-import/car/guo-jian/
+    wei-zhang: data/import/car/wei-zhang/
+    chao-bao: data/import/car/chao-bao/
+    guo-jian: data/import/car/guo-jian/
 house:
   data-import:
-    czys: data-import/house/czys/
-    wxys: data-import/house/wxys/
+    czys: data/import/house/czys/
+    wxys: data/import/house/wxys/

+ 6 - 6
src/main/resources/application-prod.yml

@@ -28,16 +28,16 @@ pages:
     siteUrl: house-car/house/dist/index.html#/administrative/overview
 car:
   data-import:
-    copy-script-path: scripts/copy.sh
+    copy-script-path: copy.sh
     db-host: 127.0.0.1
     db-port: 5432
     db-username: postgres
     db-password: Test!234
     db-name: financialdb
-    wei-zhang: data-import/car/wei-zhang/
-    chao-bao: data-import/car/chao-bao/
-    guo-jian: data-import/car/guo-jian/
+    wei-zhang: data/import/car/wei-zhang/
+    chao-bao: data/import/car/chao-bao/
+    guo-jian: data/import/car/guo-jian/
 house:
   data-import:
-    czys: data-import/house/czys/
-    wxys: data-import/house/wxys/
+    czys: data/import/house/czys/
+    wxys: data/import/house/wxys/

+ 6 - 6
src/main/resources/application-test.yml

@@ -21,16 +21,16 @@ pages:
     siteUrl: house-car/house/dist/index.html#/administrative/overview
 car:
   data-import:
-    copy-script-path: scripts/copy.sh
+    copy-script-path: copy.sh
     db-host: 127.0.0.1
     db-port: 5432
     db-username: postgres
     db-password: Test!234
     db-name: financialdb
-    wei-zhang: data-import/car/wei-zhang/
-    chao-bao: data-import/car/chao-bao/
-    guo-jian: data-import/car/guo-jian/
+    wei-zhang: data/import/car/wei-zhang/
+    chao-bao: data/import/car/chao-bao/
+    guo-jian: data/import/car/guo-jian/
 house:
   data-import:
-    czys: data-import/house/czys/
-    wxys: data-import/house/wxys/
+    czys: data/import/house/czys/
+    wxys: data/import/house/wxys/