浏览代码

农家-顧客 CSV导入

于俊龙 2 月之前
父节点
当前提交
ba21126494

+ 9 - 0
farm-common-biz/src/main/java/jp/yamoto/farm/common/biz/mapper/MastAddressBaseMapper.java

@@ -30,6 +30,15 @@ public interface MastAddressBaseMapper {
     public MastAddressEntity selectInfo(MastAddressEntity addressEntity);
 
     /**
+     *アドレスマスタを検索(都道府県名 Or 市区町村名)
+     *
+     * @param addressEntity アドレスマスタ
+     * @return アドレスマスタ
+     */
+    public MastAddressEntity selectInfoByName(MastAddressEntity addressEntity);
+
+
+    /**
      * 郵便番号を使用して住所を検索します。
      *
      * @param postalCode 郵便番号

+ 8 - 0
farm-common-biz/src/main/java/jp/yamoto/farm/common/biz/service/IMastAddressBaseService.java

@@ -28,6 +28,14 @@ public interface IMastAddressBaseService {
      */
     public MastAddressEntity selectMastAddress(MastAddressEntity addressEntity);
 
+    /**
+     * アドレスマスタを検索(都道府県名 Or 市区町村名)
+     *
+     * @param addressEntity アドレスマスタ
+     * @return アドレスマスタ
+     */
+    public MastAddressEntity selectMastAddressByName(MastAddressEntity addressEntity);
+
      /**
      * アドレスマスタを検索リスト
      *

+ 12 - 1
farm-common-biz/src/main/java/jp/yamoto/farm/common/biz/service/impl/MastAddressBaseServiceImpl.java

@@ -51,7 +51,18 @@ public class MastAddressBaseServiceImpl implements IMastAddressBaseService {
         return mastAddressBaseMapper.selectInfo(addressEntity) ;
     }
 
-     /**
+    /**
+     * アドレスマスタを検索(都道府県名 Or 市区町村名)
+     *
+     * @param addressEntity アドレスマスタ
+     * @return アドレスマスタ
+     */
+    @Override
+    public MastAddressEntity selectMastAddressByName(MastAddressEntity addressEntity) {
+        return mastAddressBaseMapper.selectInfoByName(addressEntity) ;
+    }
+
+    /**
      * アドレスマスタを検索リスト
      *
      * @param mastAddress アドレスマスタパラメータ対象

+ 26 - 0
farm-common-biz/src/main/resources/mapper/BssMastAddressBaseMapper.xml

@@ -170,6 +170,32 @@
         </where>
     </select>
 
+    <select id="selectInfoByName" parameterType="MastAddressEntity"  resultMap="MastAddressEntityResult">
+        select
+
+        <if test="kenName != null ">
+            ken_id,
+            ken_name
+        </if>
+        <if test="cityName != null ">
+            city_id,
+            city_name
+        </if>
+        from mast_address
+        <where>
+            and  discontinued_flg = '0'
+
+            <if test="kenName != null ">
+                and ken_name = #{kenName}
+            </if>
+            <if test="cityName != null ">
+                and city_name = #{cityName}
+            </if>
+
+        </where>
+        LIMIT 1
+    </select>
+
     <select id="selectMastAddressByPostalCode" parameterType="String"  resultMap="MastAddressEntityResult">
         select
             distinct

+ 10 - 0
farm-common/src/main/java/jp/yamoto/farm/common/constant/Constants.java

@@ -22,6 +22,11 @@ public class Constants
     public static final String GBK = "GBK";
 
     /**
+     * Shift_JIS 文字セット
+     */
+    public static final String SHIFT_JIS = "Shift_JIS";
+
+    /**
      * システム言語
      */
     public static final Locale DEFAULT_LOCALE = Locale.SIMPLIFIED_CHINESE;
@@ -181,4 +186,9 @@ public class Constants
      */
     public static final String[] JOB_ERROR_STR = { "java.net.URL", "javax.naming.InitialContext", "org.yaml.snakeyaml",
             "org.springframework", "org.apache", "jp.yamoto.farm.common.utils.file", "jp.yamoto.farm.common.config", "jp.yamoto.farm.generator" };
+
+    /**
+     * カンマ
+     */
+    public static final char COMMA = ',';
 }

+ 24 - 0
farm-common/src/main/java/jp/yamoto/farm/common/core/domain/ImportFileErrorVo.java

@@ -0,0 +1,24 @@
+package jp.yamoto.farm.common.core.domain;
+
+import lombok.Data;
+
+/**
+ * インポートのエラー情報VO
+ *
+ * @author nextosd
+ */
+@Data
+public class ImportFileErrorVo {
+    /**
+     * エラー対象(ファイル名或はシート名)
+     */
+    private String objectName;
+    /**
+     * エラー行
+     */
+    private String rowLine;
+    /**
+     * エラーメッセージ
+     */
+    private String message;
+}

+ 1 - 1
farm-common/src/main/java/jp/yamoto/farm/common/mybatis/interceptor/MyBatisInterceptor.java

@@ -146,7 +146,7 @@ public class MyBatisInterceptor implements Interceptor {
             if (principal != null && principal instanceof LoginUser) {
                 LoginUser loginUser = (LoginUser) authentication.getPrincipal();
                 if (loginUser != null) {
-                    loginUserId = loginUser.getUserId();
+                    loginUserId = loginUser.getUsername();
                 }
             }
             return loginUserId;

+ 98 - 12
farm-common/src/main/java/jp/yamoto/farm/common/utils/file/FileUtils.java

@@ -1,30 +1,28 @@
 package jp.yamoto.farm.common.utils.file;
 
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.UnsupportedEncodingException;
-import java.net.URLEncoder;
-import java.nio.charset.StandardCharsets;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
-import jp.yamoto.farm.common.constant.IdPrefixConstants;
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang3.ArrayUtils;
 import jp.yamoto.farm.common.config.AppConfig;
+import jp.yamoto.farm.common.constant.IdPrefixConstants;
 import jp.yamoto.farm.common.utils.DateUtils;
 import jp.yamoto.farm.common.utils.StringUtils;
 import jp.yamoto.farm.common.utils.uuid.IdUtils;
+import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.ArrayUtils;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.*;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
 
 /**
  * ファイル処理ツールクラス
  * 
  * @author nextosd
  */
+@Slf4j
 public class FileUtils
 {
     public static String FILENAME_PATTERN = "[a-zA-Z0-9_\\-\\|\\.\\u4e00-\\u9fa5]+";
@@ -285,4 +283,92 @@ public class FileUtils
         String baseName = FilenameUtils.getBaseName(fileName);
         return baseName;
     }
+
+    /**
+     * CSVファイルのShiftJISをチェックする
+     *
+     * @param file チェックファイル対象
+     * @return  UTF-8 / Shift_JIS
+     */
+    public static boolean hasShiftJIS(MultipartFile file){
+        try (InputStream fis = file.getInputStream()) {
+            byte[] firstBytes = new byte[1000];
+            int bytesRead = fis.read(firstBytes);
+
+            // check Shift_JIS
+            for (int i = 0; i < bytesRead - 1; i++) {
+                byte b1 = firstBytes[i];
+                byte b2 = firstBytes[i + 1];
+
+                // Shift_JIS
+                if (((b1 >= (byte)0x81 && b1 <= (byte)0x9F) ||
+                        (b1 >= (byte)0xE0 && b1 <= (byte)0xFC)) &&
+                        ((b2 >= (byte)0x40 && b2 <= (byte)0x7E) ||
+                                (b2 >= (byte)0x80 && b2 <= (byte)0xFC))) {
+                    return true;
+                }
+            }
+
+            return false;
+        } catch (IOException e) {
+            log.error("Error reading ShiftJIS from file: {}", file.getOriginalFilename(), e);
+            return false;
+        }
+    }
+
+    /**
+     * CSVファイルのUTF-8 BOMをチェックする
+     *
+     * @param file チェックファイル対象
+     * @return true:UTF-8 BOMあり、false:UTF-8 BOMなし
+     */
+    public static boolean hasUtf8Bom(MultipartFile file) {
+        byte[] bom = new byte[3];
+        try (InputStream inputStream = file.getInputStream()) {
+            int bytesRead = inputStream.read(bom, 0, 3);
+            if (bytesRead != 3) {
+                log.error("Failed to read 3 bytes for BOM check from file: {}", file.getOriginalFilename());
+                return false;
+            }
+        } catch (IOException e) {
+            log.error("Error reading BOM from file: {}", file.getOriginalFilename(), e);
+            return false;
+        }
+
+        // UTF-8 BOM は EF BB BF
+        return bom[0] == (byte) 0xEF && bom[1] == (byte) 0xBB && bom[2] == (byte) 0xBF;
+
+    }
+
+    /**
+     * 指定されたファイルがLF(改行)を使用しているかどうかをチェックします。
+     *
+     * @param file チェックするファイル
+     * @return LFを使用している場合はtrue、それ以外の場合はfalse
+     */
+    public static boolean  isLf(MultipartFile file) {
+        try (BufferedReader reader = new BufferedReader(new InputStreamReader(file.getInputStream()))) {
+            int ch;
+            while ((ch = reader.read()) != -1) {
+                if (ch == '\n') {
+                    // LFを検出した場合
+                    return true;
+                } else if (ch == '\r') {
+                    // CRを検出した場合、次の文字をチェック
+                    int nextCh = reader.read();
+                    if (nextCh != '\n') {
+                        // 次の文字がLFでない場合は、CRLFではない
+                        return false;
+                    } else {
+                        // 次の文字がLFである場合は、CRLFであり、LFではない
+                        return false;
+                    }
+                }
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        // 改行符が見つからない場合は、デフォルトでfalseを返す
+        return false;
+    }
 }

+ 5 - 0
farm-sankin-biz/pom.xml

@@ -24,6 +24,11 @@
         </dependency>
 
         <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-csv</artifactId>
+        </dependency>
+
+        <dependency>
             <groupId>org.projectlombok</groupId>
             <artifactId>lombok</artifactId>
             <scope>provided</scope>

+ 9 - 0
farm-sankin-biz/src/main/java/jp/yamoto/farm/sankin/biz/constants/SankinConstants.java

@@ -4,4 +4,13 @@ package jp.yamoto.farm.sankin.biz.constants;
  * 農家支援システム 新・産直くん(仮称)共通定数情報
  */
 public class SankinConstants {
+    /**
+     * 数字(10)
+     */
+    public static final int NUMBER_10 = 10;
+
+    /**
+     * 数字(1024)
+     */
+    public static final int NUMBER_1024 = 1024;
 }

+ 105 - 0
farm-sankin-biz/src/main/java/jp/yamoto/farm/sankin/biz/domain/bo/MastCustomerImportBo.java

@@ -0,0 +1,105 @@
+package jp.yamoto.farm.sankin.biz.domain.bo;
+
+import lombok.Data;
+
+/**
+ * 顧客マスタ インポート CSV Data
+ */
+@Data
+public class MastCustomerImportBo {
+
+    /**
+     * 氏名(姓)
+     */
+    private String firstName;
+
+    /**
+     * 氏名(名)
+     */
+    private String lastName;
+
+    /**
+     * 顧客名
+     */
+    private String customerName;
+
+    /**
+     * 電話番号
+     */
+    private String phoneNumber;
+
+    /**
+     * システム顧客番号
+     */
+    private String sysCustomerId;
+
+    /**
+     * 会員ID
+     */
+    private String memberId;
+
+    /**
+     * 氏名フリガナ(セイ)
+     */
+    private String furiganaSei;
+
+    /**
+     * 氏名フリガナ(メイ)
+     */
+    private String furiganaMei;
+
+    /**
+     * 会社名
+     */
+    private String companyName;
+
+    /**
+     * 部署名
+     */
+    private String departmentName;
+
+    /**
+     * 郵便番号
+     */
+    private String postalCode;
+
+    /**
+     * 都道府県
+     */
+    private String kenName;
+
+    /**
+     * 市区町村
+     */
+    private String cityName;
+
+    /**
+     * 町域・番地
+     */
+    private String townName;
+
+    /**
+     * ビル等
+     */
+    private String buildingEtc;
+
+    /**
+     * 住所
+     */
+    private String address;
+
+    /**
+     * メールアドレス
+     */
+    private String mailAddress;
+
+    /**
+     * 携帯電話番号
+     */
+    private String mobile;
+
+    /**
+     * FAX番号
+     */
+    private String fax;
+}

+ 10 - 0
farm-sankin-biz/src/main/java/jp/yamoto/farm/sankin/biz/service/IMastCustomerService.java

@@ -1,7 +1,9 @@
 package jp.yamoto.farm.sankin.biz.service;
 
+import jp.yamoto.farm.common.core.domain.ImportFileErrorVo;
 import jp.yamoto.farm.sankin.biz.domain.bo.MastCustomerSearchBo;
 import jp.yamoto.farm.sankin.biz.domain.vo.MastCustomerVo;
+import org.springframework.web.multipart.MultipartFile;
 
 import java.util.List;
 
@@ -27,4 +29,12 @@ public interface IMastCustomerService {
      * @return 顧客マスタリスト
      */
     public List<MastCustomerVo> selectCustomerList(MastCustomerSearchBo mastCustomerBo);
+
+    /**
+     * 顧客情報を取り込む
+     *
+     * @param excelFile エクセルファイル
+     * @return エラー情報のリスト
+     */
+    public List<ImportFileErrorVo> importCsvData(MultipartFile excelFile);
 }

+ 269 - 3
farm-sankin-biz/src/main/java/jp/yamoto/farm/sankin/biz/service/impl/MastCustomerServiceImpl.java

@@ -1,16 +1,40 @@
 package jp.yamoto.farm.sankin.biz.service.impl;
 
-import jp.yamoto.farm.common.utils.DecryptUtils;
-import jp.yamoto.farm.common.utils.SecurityUtils;
-import jp.yamoto.farm.common.utils.ValueUtils;
+import jp.yamoto.farm.common.biz.domain.entity.MastAddressEntity;
+import jp.yamoto.farm.common.biz.domain.entity.MastCustomerEntity;
+import jp.yamoto.farm.common.biz.enums.SankinPgIdEnum;
+import jp.yamoto.farm.common.biz.mapper.MastCustomerBaseMapper;
+import jp.yamoto.farm.common.biz.service.IMastAddressBaseService;
+import jp.yamoto.farm.common.constant.Constants;
+import jp.yamoto.farm.common.core.domain.ImportFileErrorVo;
+import jp.yamoto.farm.common.exception.ServiceException;
+import jp.yamoto.farm.common.utils.*;
+import jp.yamoto.farm.common.utils.file.FileUtils;
+import jp.yamoto.farm.common.utils.uuid.IdUtils;
+import jp.yamoto.farm.sankin.biz.constants.SankinConstants;
+import jp.yamoto.farm.sankin.biz.domain.bo.MastCustomerImportBo;
 import jp.yamoto.farm.sankin.biz.domain.bo.MastCustomerSearchBo;
 import jp.yamoto.farm.sankin.biz.domain.vo.MastCustomerVo;
 import jp.yamoto.farm.sankin.biz.mapper.MastCustomerMapper;
 import jp.yamoto.farm.sankin.biz.service.IMastCustomerService;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.csv.CSVFormat;
+import org.apache.commons.csv.CSVParser;
+import org.apache.commons.csv.CSVRecord;
+import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.CollectionUtils;
+import org.springframework.web.multipart.MultipartFile;
 
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * 顧客マスタ Service業務処理
@@ -18,11 +42,18 @@ import java.util.List;
  * @author nextosd
  */
 @Service
+@Slf4j
 public class MastCustomerServiceImpl implements IMastCustomerService {
 
     @Autowired
     private MastCustomerMapper mastCustomerMapper;
 
+    @Autowired
+    private MastCustomerBaseMapper mastCustomerBaseMapper;
+
+    @Autowired
+    private IMastAddressBaseService mastAddressBaseService;
+
     /**
      * 顧客マスタの検索
      *
@@ -69,4 +100,239 @@ public class MastCustomerServiceImpl implements IMastCustomerService {
 
         return result;
     }
+
+    /**
+     * 顧客マスタ情報を取込む
+     *
+     * @param csvFile エクセルファイル
+     * @return エラー情報リスト
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public List<ImportFileErrorVo> importCsvData(MultipartFile csvFile) {
+        List<ImportFileErrorVo> errorInfoList = new ArrayList<>();
+        String fileName = csvFile.getOriginalFilename();
+
+        if (fileName == null || !StringUtils.toCamelCase(fileName).endsWith("csv")) {
+            // 拡張子チェック CSVファイルを選択してください。
+            throw new ServiceException(MessageUtils.message("E0009", MessageUtils.message("csvExtension")));
+        }
+
+        if (!FileUtils.hasShiftJIS(csvFile)) {
+            // {0}CSVファイルのフォーマット({1})が不正です。正しいフォーマットのCSVファイルを選択してください。
+            throw new ServiceException(MessageUtils.message("E0026", csvFile.getOriginalFilename(), "Shift_JIS"));
+        }
+
+        long totalSize = csvFile.getSize();
+
+        if (totalSize > SankinConstants.NUMBER_10 * SankinConstants.NUMBER_1024 * SankinConstants.NUMBER_1024) {
+            // ファイルのサイズは10MB以下を選択してください。
+            throw new ServiceException(MessageUtils.message("E0024", "10MB"));
+        }
+
+        // ヘッダー情報と明細情報のエラー情報を格納するリスト
+        List<ImportFileErrorVo> errorResultDetailInfoList = new ArrayList<>();
+        List<MastCustomerImportBo> mastCustomerImportDataList = new ArrayList<>();
+
+        // 選択されたファイルのサイズ = 0MB または CSVにデータが0件の場合
+        String csvFileName = csvFile.getOriginalFilename();
+        if (!Objects.requireNonNull(csvFileName).startsWith("customer")
+                && !Objects.requireNonNull(csvFileName).startsWith("customer")) {
+            // アップロード対象が顧客情報の場合、顧客情報以外ファイル(不正のファイル名)を選択しないでください。
+            String param = MessageUtils.message("customer");
+            throw new ServiceException(MessageUtils.message("E0027", param, param, csvFileName));
+        }
+
+        if (csvFile.getSize() == 0) {
+            // {不正のファイル名}ファイルのサイズが0Mであるか。データを追加した後、再度アップロードしてください。
+            throw new ServiceException(MessageUtils.message("E0025", csvFileName));
+        }
+
+        try (Reader fileReader = new InputStreamReader(csvFile.getInputStream(), Charset.forName(Constants.SHIFT_JIS));
+             // 区切り文字をカンマに設定
+             // 引用文字をダブルクォートに設定
+             // エスケープ文字をバックスラッシュに設定を無視
+             // フィールド周囲の空白文字を無視
+             // 空行を無視しない
+             CSVParser csvParser = new CSVParser(fileReader, CSVFormat.DEFAULT.builder()
+                     .setDelimiter(Constants.COMMA)
+                     .setQuote('\"')
+                     .setIgnoreSurroundingSpaces(true)
+                     .setIgnoreEmptyLines(false)
+                     .build());
+        ) {
+
+            List<CSVRecord> csvRecords = csvParser.getRecords();
+
+            if (!CollectionUtils.isEmpty(csvRecords) && csvRecords.size() == 2) {
+                // {不正のファイル名}ファイルのサイズが0Mであるか。データを追加した後、再度アップロードしてください。
+                throw new ServiceException(MessageUtils.message("E0025", csvFileName));
+            }
+
+            for (CSVRecord record : csvRecords) {
+                // レコードのフィールド数を取得
+                int fieldCount = record.size();
+                long csvRowLine = record.getRecordNumber();
+                String fileRowLine = ConvertUtils.toStr(csvRowLine);
+
+                if (csvRowLine <= 1) {
+                    // 最初の2行をスキップ
+                    continue;
+                }
+
+                MastCustomerImportBo mastCustomerImportBo = new MastCustomerImportBo();
+
+                // システム顧客番号 sys_customer_id
+                mastCustomerImportBo.setSysCustomerId(record.get(0));
+                validateFieldLength(mastCustomerImportBo.getSysCustomerId(), "label.sysCustomerId", 32, csvFileName, fileRowLine, errorInfoList);
+
+                // 会員ID
+                mastCustomerImportBo.setMemberId(record.get(1));
+                validateFieldLength(mastCustomerImportBo.getMemberId(), "label.memberId", 20, csvFileName, fileRowLine, errorInfoList);
+
+                // 氏名(姓)
+                mastCustomerImportBo.setFirstName(record.get(2));
+                validateFieldLength(mastCustomerImportBo.getFirstName(), "label.firstName", 64, csvFileName, fileRowLine, errorInfoList);
+
+                // 氏名(名)
+                mastCustomerImportBo.setLastName(record.get(3));
+                validateFieldLength(mastCustomerImportBo.getLastName(), "label.lastName", 32, csvFileName, fileRowLine, errorInfoList);
+
+                // 氏名フリガナ(セイ)
+                mastCustomerImportBo.setFuriganaSei(record.get(4));
+                validateFieldLength(mastCustomerImportBo.getFuriganaSei(), "label.furiganaSei", 56, csvFileName, fileRowLine, errorInfoList);
+
+                // 氏名フリガナ(メイ)
+                mastCustomerImportBo.setFuriganaMei(record.get(5));
+                validateFieldLength(mastCustomerImportBo.getFuriganaMei(), "label.furiganaMei", 56, csvFileName, fileRowLine, errorInfoList);
+
+                // 会社名
+                mastCustomerImportBo.setCompanyName(record.get(6));
+                // 部署名
+                mastCustomerImportBo.setDepartmentName(record.get(7));
+                // 郵便番号
+                mastCustomerImportBo.setPostalCode(record.get(8));
+                // 都道府県
+                mastCustomerImportBo.setKenName(record.get(9));
+                // 市区町村
+                mastCustomerImportBo.setCityName(record.get(10));
+                // 町域・番地
+                mastCustomerImportBo.setTownName(record.get(11));
+                // ビル等
+                mastCustomerImportBo.setBuildingEtc(record.get(12));
+                // メールアドレス
+                mastCustomerImportBo.setMailAddress(record.get(13));
+                // 電話番号
+                mastCustomerImportBo.setPhoneNumber(record.get(14));
+                // 携帯電話番号
+                mastCustomerImportBo.setMobile(record.get(15));
+                // FAX番号
+                mastCustomerImportBo.setFax(record.get(16));
+
+                mastCustomerImportDataList.add(mastCustomerImportBo);
+            }
+
+        } catch (IOException e) {
+            log.error("CSVファイルを読み取る際にエラーが発生しました。", e);
+            // I0004=取込に失敗しました。
+            throw new ServiceException(MessageUtils.message("I0004"));
+        }
+
+        if (!CollectionUtils.isEmpty(errorInfoList)) {
+            return errorInfoList;
+        }
+
+        for(MastCustomerImportBo data : mastCustomerImportDataList){
+            MastCustomerEntity mastCustomerEntity = new MastCustomerEntity();
+            BeanUtils.copyProperties(data, mastCustomerEntity);
+
+            // 都道府県
+            if(ValueUtils.isNotEmpty(data.getKenName())){
+                MastAddressEntity mastAddressQuery = new MastAddressEntity();
+                mastAddressQuery.setKenName(data.getKenName());
+                MastAddressEntity mastAddressEntity = mastAddressBaseService.selectMastAddressByName(mastAddressQuery);
+                if(ValueUtils.isNotEmpty(mastAddressEntity)) {
+                    mastCustomerEntity.setKenId(mastAddressEntity.getKenId());
+                }
+            }
+
+            // 市区町村
+            if(ValueUtils.isNotEmpty(data.getCityName())){
+                MastAddressEntity mastAddressQuery = new MastAddressEntity();
+                mastAddressQuery.setCityName(data.getCityName());
+                MastAddressEntity mastAddressEntity = mastAddressBaseService.selectMastAddressByName(mastAddressQuery);
+                if(ValueUtils.isNotEmpty(mastAddressEntity)) {
+                    mastCustomerEntity.setCityId(mastAddressEntity.getCityId());
+                }
+            }
+
+            // メールアドレス
+            if(ValueUtils.isNotEmpty(data.getMailAddress())){
+                mastCustomerEntity.setMailAddress(DecryptUtils.encryptAES(data.getMailAddress()));
+            }
+
+            // 電話番号
+            if(ValueUtils.isNotEmpty(data.getPhoneNumber())){
+                mastCustomerEntity.setPhoneNumber(DecryptUtils.encryptAES(data.getPhoneNumber()));
+            }
+
+            // ID
+            mastCustomerEntity.setId(IdUtils.nextIdStr());
+            // 顧客名
+            mastCustomerEntity.setCustomerName(StringUtils.join(data.getFirstName(), data.getLastName()));
+            // 顧客ID = 会員ID
+            mastCustomerEntity.setCustomerId(data.getMemberId());
+            // 住所
+            mastCustomerEntity.setAddress(StringUtils.join(data.getKenName(), data.getCityName(), data.getTownName(), data.getBuildingEtc()));
+            // 農家ID
+            mastCustomerEntity.setFarmerId(SecurityUtils.getUsername());
+            // 登録プログラムID
+            mastCustomerEntity.setCreatePgId(SankinPgIdEnum.N015.getCode());
+            // 更新プログラムID
+            mastCustomerEntity.setUpdatePgId(SankinPgIdEnum.N015.getCode());
+            // Version
+            mastCustomerEntity.setVersion(1);
+            // システムソースフラグ = 1:らくうるカート
+            mastCustomerEntity.setSystemSourceFlg("1");
+
+            mastCustomerBaseMapper.insert(mastCustomerEntity);
+        }
+
+        return errorInfoList;
+    }
+
+    /**
+     * 検査結果詳細情報のアップロード検証
+     *
+     * @param fieldValue     フィールド値
+     * @param fieldName      フィールド名
+     * @param maxLength      最大桁数
+     * @param detailFileName ファイル名
+     * @param fileRowLine    行番号
+     * @param errorInfoList  エラー情報リスト
+     */
+    private void validateFieldLength(String fieldValue, String fieldName, int maxLength, String detailFileName,
+                               String fileRowLine, List<ImportFileErrorVo> errorInfoList) {
+        if (fieldValue.length() > maxLength) {
+            // E0051={0}の桁数は{1}桁以上、もしくはサロゲートペアが含まれ最長バイト数を超過
+            String message = MessageUtils.message("E0051", MessageUtils.message(fieldName), maxLength);
+            makeErrorInfo(detailFileName, fileRowLine, message, errorInfoList);
+        }
+    }
+
+    /**
+     * エラー情報を生成する
+     *
+     * @param fileName    不正ファイル名
+     * @param fileRowLine 不正行番号
+     * @param message     メッセージ
+     * @param errorList   格納エラーリスト
+     */
+    private void makeErrorInfo(String fileName, String fileRowLine, String message, List<ImportFileErrorVo> errorList) {
+        ImportFileErrorVo errorInfo = new ImportFileErrorVo();
+        errorInfo.setObjectName(fileName);
+        errorInfo.setRowLine(fileRowLine);
+        errorInfo.setMessage(message);
+        errorList.add(errorInfo);
+    }
 }

+ 28 - 4
farm-sankin/src/main/java/jp/yamoto/farm/sankin/web/controller/CustomerController.java

@@ -3,15 +3,17 @@ package jp.yamoto.farm.sankin.web.controller;
 
 import jp.yamoto.farm.common.core.controller.BaseController;
 import jp.yamoto.farm.common.core.domain.AjaxResult;
+import jp.yamoto.farm.common.core.domain.ImportFileErrorVo;
 import jp.yamoto.farm.common.core.page.TableDataInfo;
+import jp.yamoto.farm.common.utils.MessageUtils;
 import jp.yamoto.farm.sankin.biz.domain.bo.MastCustomerSearchBo;
 import jp.yamoto.farm.sankin.biz.domain.vo.MastCustomerVo;
 import jp.yamoto.farm.sankin.biz.service.IMastCustomerService;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.util.CollectionUtils;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
 
 import java.util.List;
 
@@ -22,6 +24,7 @@ import java.util.List;
  */
 @RestController
 @RequestMapping("/api/customer")
+@Slf4j
 public class CustomerController extends BaseController {
 
     @Autowired
@@ -44,4 +47,25 @@ public class CustomerController extends BaseController {
     public AjaxResult getInfo(@PathVariable("id") String id) {
         return AjaxResult.success(mastCustomerService.selectCustomerById(id));
     }
+
+    /**
+     * インポート
+     *
+     * @param file アップロードするファイル
+     * @return アップロード結果
+     */
+    @PostMapping("/import")
+    public AjaxResult testS3Upload(@RequestParam("file") MultipartFile file) {
+        List<ImportFileErrorVo> errorList = mastCustomerService.importCsvData(file);
+
+        if (!CollectionUtils.isEmpty(errorList)) {
+            // I0004=取込に失敗しました。
+            AjaxResult ajax = AjaxResult.success(MessageUtils.message("I0004"), "error");
+            ajax.put("errorList", errorList);
+            return ajax;
+        }
+
+        // I0003=取込に成功しました。
+        return AjaxResult.success(MessageUtils.message("I0003"));
+    }
 }

+ 9 - 0
farm-sankin/src/main/resources/i18n/messages.properties

@@ -84,6 +84,15 @@ label.nokaMei=\u8FB2\u5BB6\u540D
 label.nokaMeiKn=\u8FB2\u5BB6\u540D\uFF08\u30AB\u30CA\uFF09
 label.nokaCd=\u8FB2\u5BB6ID
 
+label.customer=\u9867\u5BA2
+label.sysCustomerId=\u30B7\u30B9\u30C6\u30E0\u9867\u5BA2\u756A\u53F7
+label.memberId=\u4F1A\u54E1ID
+label.firstName=\u6C0F\u540D\uFF08\u59D3\uFF09
+label.lastName=\u6C0F\u540D\uFF08\u540D\uFF09
+label.phoneNumber=\u96FB\u8A71\u756A\u53F7
+label.furiganaSei=\u6C0F\u540D\u30D5\u30EA\u30AC\u30CA\uFF08\u30BB\u30A4\uFF09
+label.furiganaMei=\u6C0F\u540D\u30D5\u30EA\u30AC\u30CA\uFF08\u30E1\u30A4\uFF09
+
 label.branchNo=\u679D\u756A
 label.orderNo=\u4E26\u3073\u9806
 label.itemLabel=\u9805\u76EE\u30E9\u30D9\u30EB

+ 7 - 0
pom.xml

@@ -35,6 +35,7 @@
         <jaxb-api.version>2.3.1</jaxb-api.version>
         <jakarta.version>6.0.0</jakarta.version>
         <springdoc.version>2.8.9</springdoc.version>
+        <commons-csv.version>1.10.0</commons-csv.version>
     </properties>
 
     <!-- dependencies -->
@@ -153,6 +154,12 @@
                 <version>${kaptcha.version}</version>
             </dependency>
 
+            <dependency>
+                <groupId>org.apache.commons</groupId>
+                <artifactId>commons-csv</artifactId>
+                <version>${commons-csv.version}</version>
+            </dependency>
+
             <!-- JOB-->
             <dependency>
                 <groupId>jp.yamoto</groupId>