POI讀取excel百萬級-SAX方式解析
阿新 • • 發佈:2018-12-21
一. 簡介
在excel解析的時候,採用SAX方方式會將excel轉換為xml進行解析避免了記憶體溢位。
速度在3秒1W的資料寫入,100W條記錄,大概50M的資料,耗時大概4分半(如果不需要校驗,可能會更快);
暫時先直接將專案中的拷貝出來,使用的時候直接調工具類即可。目前正在搞自己的一個專案,後期會將匯入,匯出都弄上去,再優化下,放到git上。
另外,膜拜下原生jdbc, 昨天問了下,聽說可以達到1秒10W數量級的寫入;
是在網上找的一個,然後自己封裝了下, 加了一個委託,目前存在一個BUG: 就是如果excel中沒有資料,會自動跳過該空格。目前解決方案是:excel中為空的使用“-”來標識,後期解決。
二. 程式碼DEMO
2.1 POM依賴
<!-- poi --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>3.17</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>3.17</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml-schemas</artifactId> <version>3.17</version> </dependency> <!-- sax --> <dependency> <groupId>sax</groupId> <artifactId>sax</artifactId> <version>2.0.1</version> </dependency> <dependency> <groupId>xml-apis</groupId> <artifactId>xml-apis</artifactId> <version>1.4.01</version> </dependency> <dependency> <groupId>org.apache.xmlbeans</groupId> <artifactId>xmlbeans</artifactId> <version>2.6.0</version> </dependency> <dependency> <groupId>xerces</groupId> <artifactId>xercesImpl</artifactId> <version>2.11.0</version> </dependency>
2.2 EXCEL常量類
package com.yzx.osp.common.constant; /** * @author qjwyss * @date 2018/9/19 * @description EXCEL常量類 */ public class ExcelConstant { /** * excel2007副檔名 */ public static final String EXCEL07_EXTENSION = ".xlsx"; /** * 每個sheet儲存的記錄數 100W */ public static final Integer PER_SHEET_ROW_COUNT = 1000000; /** * 每次向EXCEL寫入的記錄數(查詢每頁資料大小) 20W */ public static final Integer PER_WRITE_ROW_COUNT = 200000; /** * 每個sheet的寫入次數 5 */ public static final Integer PER_SHEET_WRITE_COUNT = PER_SHEET_ROW_COUNT / PER_WRITE_ROW_COUNT; /** * 讀取excel的時候每次批量插入資料庫記錄數 */ public static final Integer PER_READ_INSERT_BATCH_COUNT = 10000; }
2.3 讀取EXCEL輔助類
package com.yzx.osp.common.util;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.ss.usermodel.BuiltinFormats;
import org.apache.poi.ss.usermodel.DataFormatter;
import org.apache.poi.xssf.eventusermodel.XSSFReader;
import org.apache.poi.xssf.model.SharedStringsTable;
import org.apache.poi.xssf.model.StylesTable;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.xssf.usermodel.XSSFRichTextString;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLReaderFactory;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
/**
* @author qjwyss
* @date 2018/12/19
* @description 讀取EXCEL輔助類
*/
public class ExcelXlsxReaderWithDefaultHandler extends DefaultHandler {
private ExcelReadDataDelegated excelReadDataDelegated;
public ExcelReadDataDelegated getExcelReadDataDelegated() {
return excelReadDataDelegated;
}
public void setExcelReadDataDelegated(ExcelReadDataDelegated excelReadDataDelegated) {
this.excelReadDataDelegated = excelReadDataDelegated;
}
public ExcelXlsxReaderWithDefaultHandler(ExcelReadDataDelegated excelReadDataDelegated) {
this.excelReadDataDelegated = excelReadDataDelegated;
}
/**
* 單元格中的資料可能的資料型別
*/
enum CellDataType {
BOOL, ERROR, FORMULA, INLINESTR, SSTINDEX, NUMBER, DATE, NULL
}
/**
* 共享字串表
*/
private SharedStringsTable sst;
/**
* 上一次的索引值
*/
private String lastIndex;
/**
* 檔案的絕對路徑
*/
private String filePath = "";
/**
* 工作表索引
*/
private int sheetIndex = 0;
/**
* sheet名
*/
private String sheetName = "";
/**
* 總行數
*/
private int totalRows = 0;
/**
* 一行內cell集合
*/
private List<String> cellList = new ArrayList<String>();
/**
* 判斷整行是否為空行的標記
*/
private boolean flag = false;
/**
* 當前行
*/
private int curRow = 1;
/**
* 當前列
*/
private int curCol = 0;
/**
* T元素標識
*/
private boolean isTElement;
/**
* 異常資訊,如果為空則表示沒有異常
*/
private String exceptionMessage;
/**
* 單元格資料型別,預設為字串型別
*/
private CellDataType nextDataType = CellDataType.SSTINDEX;
private final DataFormatter formatter = new DataFormatter();
/**
* 單元格日期格式的索引
*/
private short formatIndex;
/**
* 日期格式字串
*/
private String formatString;
//定義前一個元素和當前元素的位置,用來計算其中空的單元格數量,如A6和A8等
private String preRef = null, ref = null;
//定義該文件一行最大的單元格數,用來補全一行最後可能缺失的單元格
private String maxRef = null;
/**
* 單元格
*/
private StylesTable stylesTable;
/**
* 總行號
*/
private Integer totalRowCount;
/**
* 遍歷工作簿中所有的電子表格
* 並快取在mySheetList中
*
* @param filename
* @throws Exception
*/
public int process(String filename) throws Exception {
filePath = filename;
OPCPackage pkg = OPCPackage.open(filename);
XSSFReader xssfReader = new XSSFReader(pkg);
stylesTable = xssfReader.getStylesTable();
SharedStringsTable sst = xssfReader.getSharedStringsTable();
XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
this.sst = sst;
parser.setContentHandler(this);
XSSFReader.SheetIterator sheets = (XSSFReader.SheetIterator) xssfReader.getSheetsData();
while (sheets.hasNext()) { //遍歷sheet
curRow = 1; //標記初始行為第一行
sheetIndex++;
InputStream sheet = sheets.next(); //sheets.next()和sheets.getSheetName()不能換位置,否則sheetName報錯
sheetName = sheets.getSheetName();
InputSource sheetSource = new InputSource(sheet);
parser.parse(sheetSource); //解析excel的每條記錄,在這個過程中startElement()、characters()、endElement()這三個函式會依次執行
sheet.close();
}
return totalRows; //返回該excel檔案的總行數,不包括首列和空行
}
/**
* 第一個執行
*
* @param uri
* @param localName
* @param name
* @param attributes
* @throws SAXException
*/
@Override
public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException {
// 獲取總行號 格式: A1:B5 取最後一個值即可
if("dimension".equals(name)) {
String dimensionStr = attributes.getValue("ref");
totalRowCount = Integer.parseInt(dimensionStr.substring(dimensionStr.indexOf(":") + 2)) - 1;
}
//c => 單元格
if ("c".equals(name)) {
//前一個單元格的位置
if (preRef == null) {
preRef = attributes.getValue("r");
} else {
preRef = ref;
}
//當前單元格的位置
ref = attributes.getValue("r");
//設定單元格型別
this.setNextDataType(attributes);
}
//當元素為t時
if ("t".equals(name)) {
isTElement = true;
} else {
isTElement = false;
}
//置空
lastIndex = "";
}
/**
* 第二個執行
* 得到單元格對應的索引值或是內容值
* 如果單元格型別是字串、INLINESTR、數字、日期,lastIndex則是索引值
* 如果單元格型別是布林值、錯誤、公式,lastIndex則是內容值
*
* @param ch
* @param start
* @param length
* @throws SAXException
*/
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
lastIndex += new String(ch, start, length);
}
/**
* 第三個執行
*
* @param uri
* @param localName
* @param name
* @throws SAXException
*/
@Override
public void endElement(String uri, String localName, String name) throws SAXException {
//t元素也包含字串
if (isTElement) {//這個程式沒經過
//將單元格內容加入rowlist中,在這之前先去掉字串前後的空白符
String value = lastIndex.trim();
cellList.add(curCol, value);
curCol++;
isTElement = false;
//如果裡面某個單元格含有值,則標識該行不為空行
if (value != null && !"".equals(value)) {
flag = true;
}
} else if ("v".equals(name)) {
//v => 單元格的值,如果單元格是字串,則v標籤的值為該字串在SST中的索引
String value = this.getDataValue(lastIndex.trim(), "");//根據索引值獲取對應的單元格值
//補全單元格之間的空單元格
if (!ref.equals(preRef)) {
int len = countNullCell(ref, preRef);
for (int i = 0; i < len; i++) {
cellList.add(curCol, "");
curCol++;
}
}
cellList.add(curCol, value);
curCol++;
//如果裡面某個單元格含有值,則標識該行不為空行
if (value != null && !"".equals(value)) {
flag = true;
}
} else {
//如果標籤名稱為row,這說明已到行尾,呼叫optRows()方法
if ("row".equals(name)) {
//預設第一行為表頭,以該行單元格數目為最大數目
if (curRow == 1) {
maxRef = ref;
}
//補全一行尾部可能缺失的單元格
if (maxRef != null) {
int len = countNullCell(maxRef, ref);
for (int i = 0; i <= len; i++) {
cellList.add(curCol, "");
curCol++;
}
}
if (flag && curRow != 1) { //該行不為空行且該行不是第一行,則傳送(第一行為列名,不需要)
// 呼叫excel讀資料委託類進行讀取插入操作
excelReadDataDelegated.readExcelDate(sheetIndex, totalRowCount, curRow, cellList);
totalRows++;
}
cellList.clear();
curRow++;
curCol = 0;
preRef = null;
ref = null;
flag = false;
}
}
}
/**
* 處理資料型別
*
* @param attributes
*/
public void setNextDataType(Attributes attributes) {
nextDataType = CellDataType.NUMBER; //cellType為空,則表示該單元格型別為數字
formatIndex = -1;
formatString = null;
String cellType = attributes.getValue("t"); //單元格型別
String cellStyleStr = attributes.getValue("s"); //
String columnData = attributes.getValue("r"); //獲取單元格的位置,如A1,B1
if ("b".equals(cellType)) { //處理布林值
nextDataType = CellDataType.BOOL;
} else if ("e".equals(cellType)) { //處理錯誤
nextDataType = CellDataType.ERROR;
} else if ("inlineStr".equals(cellType)) {
nextDataType = CellDataType.INLINESTR;
} else if ("s".equals(cellType)) { //處理字串
nextDataType = CellDataType.SSTINDEX;
} else if ("str".equals(cellType)) {
nextDataType = CellDataType.FORMULA;
}
if (cellStyleStr != null) { //處理日期
int styleIndex = Integer.parseInt(cellStyleStr);
XSSFCellStyle style = stylesTable.getStyleAt(styleIndex);
formatIndex = style.getDataFormat();
formatString = style.getDataFormatString();
if (formatString.contains("m/d/yy") || formatString.contains("yyyy/mm/dd") || formatString.contains("yyyy/m/d")) {
nextDataType = CellDataType.DATE;
formatString = "yyyy-MM-dd hh:mm:ss";
}
if (formatString == null) {
nextDataType = CellDataType.NULL;
formatString = BuiltinFormats.getBuiltinFormat(formatIndex);
}
}
}
/**
* 對解析出來的資料進行型別處理
*
* @param value 單元格的值,
* value代表解析:BOOL的為0或1, ERROR的為內容值,FORMULA的為內容值,INLINESTR的為索引值需轉換為內容值,
* SSTINDEX的為索引值需轉換為內容值, NUMBER為內容值,DATE為內容值
* @param thisStr 一個空字串
* @return
*/
@SuppressWarnings("deprecation")
public String getDataValue(String value, String thisStr) {
switch (nextDataType) {
// 這幾個的順序不能隨便交換,交換了很可能會導致資料錯誤
case BOOL: //布林值
char first = value.charAt(0);
thisStr = first == '0' ? "FALSE" : "TRUE";
break;
case ERROR: //錯誤
thisStr = "\"ERROR:" + value.toString() + '"';
break;
case FORMULA: //公式
thisStr = '"' + value.toString() + '"';
break;
case INLINESTR:
XSSFRichTextString rtsi = new XSSFRichTextString(value.toString());
thisStr = rtsi.toString();
rtsi = null;
break;
case SSTINDEX: //字串
String sstIndex = value.toString();
try {
int idx = Integer.parseInt(sstIndex);
XSSFRichTextString rtss = new XSSFRichTextString(sst.getEntryAt(idx));//根據idx索引值獲取內容值
thisStr = rtss.toString();
rtss = null;
} catch (NumberFormatException ex) {
thisStr = value.toString();
}
break;
case NUMBER: //數字
if (formatString != null) {
thisStr = formatter.formatRawCellContents(Double.parseDouble(value), formatIndex, formatString).trim();
} else {
thisStr = value;
}
thisStr = thisStr.replace("_", "").trim();
break;
case DATE: //日期
thisStr = formatter.formatRawCellContents(Double.parseDouble(value), formatIndex, formatString);
// 對日期字串作特殊處理,去掉T
thisStr = thisStr.replace("T", " ");
break;
default:
thisStr = " ";
break;
}
return thisStr;
}
public int countNullCell(String ref, String preRef) {
//excel2007最大行數是1048576,最大列數是16384,最後一列列名是XFD
String xfd = ref.replaceAll("\\d+", "");
String xfd_1 = preRef.replaceAll("\\d+", "");
xfd = fillChar(xfd, 3, '@', true);
xfd_1 = fillChar(xfd_1, 3, '@', true);
char[] letter = xfd.toCharArray();
char[] letter_1 = xfd_1.toCharArray();
int res = (letter[0] - letter_1[0]) * 26 * 26 + (letter[1] - letter_1[1]) * 26 + (letter[2] - letter_1[2]);
return res - 1;
}
public String fillChar(String str, int len, char let, boolean isPre) {
int len_1 = str.length();
if (len_1 < len) {
if (isPre) {
for (int i = 0; i < (len - len_1); i++) {
str = let + str;
}
} else {
for (int i = 0; i < (len - len_1); i++) {
str = str + let;
}
}
}
return str;
}
}
注: 此處使用了setter、getter、建構函式的方式將寫資料委託介面注入進去,想了半天。
2.4 寫資料委託介面
package com.yzx.osp.common.util;
import java.util.List;
/**
* @author qjwyss
* @date 2018/12/19
* @description 讀取excel資料委託介面
*/
public interface ExcelReadDataDelegated {
/**
* 每獲取一條記錄,即寫資料
* 在flume裡每獲取一條記錄即寫,而不必快取起來,可以大大減少記憶體的消耗,這裡主要是針對flume讀取大資料量excel來說的
*
* @param sheetIndex sheet位置
* @param totalRowCount 該sheet總行數
* @param curRow 行號
* @param cellList 行資料
*/
public abstract void readExcelDate(int sheetIndex, int totalRowCount, int curRow, List<String> cellList);
}
2.5 讀取工具類
package com.yzx.osp.common.util;
import com.yzx.osp.common.constant.ExcelConstant;
import java.util.List;
/**
* @author qjwyss
* @date 2018/12/19
* @description 讀取EXCEL工具類
*/
public class ExcelReaderUtil {
public static void readExcel(String filePath, ExcelReadDataDelegated excelReadDataDelegated) throws Exception {
int totalRows = 0;
if (filePath.endsWith(ExcelConstant.EXCEL07_EXTENSION)) {
ExcelXlsxReaderWithDefaultHandler excelXlsxReader = new ExcelXlsxReaderWithDefaultHandler(excelReadDataDelegated);
totalRows = excelXlsxReader.process(filePath);
} else {
throw new Exception("檔案格式錯誤,fileName的副檔名只能是xlsx!");
}
System.out.println("讀取的資料總行數:" + totalRows);
}
public static void main(String[] args) throws Exception {
String path = "E:\\temp\\5.xlsx";
ExcelReaderUtil.readExcel(path, new ExcelReadDataDelegated() {
@Override
public void readExcelDate(int sheetIndex, int totalRowCount, int curRow, List<String> cellList) {
System.out.println("總行數為:" + totalRowCount + " 行號為:" + curRow + " 資料:" + cellList);
}
});
}
}
注: 此處可以直接使用,將sheet索引、總行數、當前行號、當前行記錄打印出來,結果如下:
三. 使用案例
3.1 特殊說明
剛開始進行匯入的 時候,因為每條記錄要進行三個欄位的校驗(是否存在)和轉換(名稱要轉換為對應的資料庫ID),剛開始的做法是 讀取一條記錄,然後發3次查詢sql進行校驗,然後再發一次插入sql進行儲存。然後悲劇了,測試匯入1W條資料要好幾分鐘。 第二天早上想來突然想到優化方案, 瓶頸在於頻繁傳送sql語句。然後就在開始匯入之前,將需要校驗的記錄先查詢出來放到map中,然後每次遍歷的時候直接從map中取出來進行校驗,不必發sql了; 還有就是批量儲存;
3.2 使用案例
忙,沒時間修改,直接將程式碼拷貝上去了,自己用的時候直接呼叫工具類即可。
@Override
public ResultVO<Void> importMobileManagerList(String filePath) throws Exception {
logger.info("開始匯入號碼列表:" + DateUtil.formatDate(new Date(), DateUtil.YYYY_MM_DD_HH_MM_SS));
List<Integer> errorRowNumber = new ArrayList<>();
List<MobileManager> mobileManagerList = new ArrayList<>();
MobileManagerVO mobileManagerVO = new MobileManagerVO();
List<String> mobileList = this.mobileManagerMapper.selectMobileList(mobileManagerVO);
SysAreaVO sysAreaVO = new SysAreaVO();
List<SysAreaVO> sysAreaVOList = this.sysAreaMapper.selectSysAreaVOList(sysAreaVO);
CustomerVO customerVO = new CustomerVO();
List<String> customerIdList = this.customerDao.selectLineOrBusinessCustomerIdList(customerVO);
List<CustomerVO> customerVOList = this.customerDao.selectCustomerIdAndAccountIdList(customerVO);
ExcelReaderUtil.readExcel(filePath, new ExcelReadDataDelegated() {
@Override
public void readExcelDate(int sheetIndex, int totalRowCount, int curRow, List<String> cellList) {
// 校驗資料合法性
Boolean legalFlag = true;
Integer provinceId = null;
Integer cityId = null;
List<String> accountIdList = null;
// 號碼、成本號碼費、成本低消費、客戶號碼費、客戶低消費不能為空
if (CommonUtil.checkStringIsNullOrLine(cellList.get(0)) || CommonUtil.checkStringIsNullOrLine(cellList.get(12))
|| CommonUtil.checkStringIsNullOrLine(cellList.get(13)) || CommonUtil.checkStringIsNullOrLine(cellList.get(14))
|| CommonUtil.checkStringIsNullOrLine(cellList.get(15))) {
legalFlag = false;
}
// 客戶型別為VBOSS並且分配了客戶時,賬戶不能為空
if (!CommonUtil.checkStringIsNullOrLine(cellList.get(7)) && !CommonUtil.checkStringIsNullOrLine(cellList.get(8))) {
if (cellList.get(7).trim().equals("VBOSS") && CommonUtil.checkStringIsNullOrLine(cellList.get(8))) {
legalFlag = false;
}
}
// 客戶型別為空的時候客戶賬戶不能有值
if (CommonUtil.checkStringIsNullOrLine(cellList.get(7))) {
if (!CommonUtil.checkStringIsNullOrLine(cellList.get(8)) || !CommonUtil.checkStringIsNullOrLine(cellList.get(9))) {
legalFlag = false;
}
}
// 客戶型別為bss的時候賬戶不能有值
if (CommonUtil.checkStringIsNullOrLine(cellList.get(7))) {
if (cellList.get(7).trim().equals("BSS")) {
if (!CommonUtil.checkStringIsNullOrLine(cellList.get(9))) {
legalFlag = false;
}
}
}
// 號碼、區號必須為數字
if (!CommonUtil.checkStringIsNullOrLine(cellList.get(0))) {
if (!CommonUtil.checkIsInteger(cellList.get(0).trim())) {
legalFlag = false;
}
}
if (!CommonUtil.checkStringIsNullOrLine(cellList.get(1))) {
if (!CommonUtil.checkIsInteger(cellList.get(1).trim())) {
legalFlag = false;
}
}
// 運營商只能為 移動、聯通、電信、鐵通、其它之一
if (!CommonUtil.checkStringIsNullOrLine(cellList.get(5))) {
if (!cellList.get(5).trim().equals("移動") && !cellList.get(5).trim().equals("聯通")
&& !cellList.get(5).trim().equals("電信") && !cellList.get(5).trim().equals("鐵通")
&& !cellList.get(5).trim().equals("其它")) {
legalFlag = false;
}
}
// 客戶型別只能是 VBOSS或BSS之一
if (!CommonUtil.checkStringIsNullOrLine(cellList.get(7))) {
if (!cellList.get(7).trim().equalsIgnoreCase("VBOSS") && !cellList.get(7).trim().equalsIgnoreCase("BSS")) {
legalFlag = false;
}
}
// 成本號碼費、成本低消費、客戶號碼費、客戶低消費只能為小數
if (!CommonUtil.checkStringIsNullOrLine(cellList.get(12))) {
if (!CommonUtil.checkIsSmallNumber(cellList.get(12).trim())) {
legalFlag = false;
}
}
if (!CommonUtil.checkStringIsNullOrLine(cellList.get(13))) {
if (!CommonUtil.checkIsSmallNumber(cellList.get(13).trim())) {
legalFlag = false;
}
}
if (!CommonUtil.checkStringIsNullOrLine(cellList.get(14))) {
if (!CommonUtil.checkIsSmallNumber(cellList.get(14).trim())) {
legalFlag = false;
}
}
if (!CommonUtil.checkStringIsNullOrLine(cellList.get(15))) {
if (!CommonUtil.checkIsSmallNumber(cellList.get(15).trim())) {
legalFlag = false;
}
}
// 資料庫校驗
// 校驗號碼是否存在
if (!CollectionUtils.isEmpty(mobileList)) {
if (mobileList.contains(cellList.get(0).trim())) {
legalFlag = false;
}
}
// 校驗省是否存在
if (!CommonUtil.checkStringIsNullOrLine(cellList.get(2))) {
if (CollectionUtils.isEmpty(sysAreaVOList)) {
legalFlag = false;
} else {
Boolean hasFlag = false;
for (SysAreaVO eachSysAreaVO : sysAreaVOList) {
if (eachSysAreaVO.getAreaName().equals(cellList.get(2).trim())) {
hasFlag = true;
provinceId = eachSysAreaVO.getSaid();
break;
}
}
if (!hasFlag) {
legalFlag = false;
}
}
}
// 校驗市是否存在
if (!CommonUtil.checkStringIsNullOrLine(cellList.get(3))) {
if (CollectionUtils.isEmpty(sysAreaVOList)) {
legalFlag = false;
} else {
Boolean hasFlag = false;
for (SysAreaVO eachSysAreaVO : sysAreaVOList) {
if (eachSysAreaVO.getAreaName().equals(cellList.get(3).trim())) {
hasFlag = true;
cityId = eachSysAreaVO.getSaid();
break;
}
}
if (!hasFlag) {
legalFlag = false;
}
}
}
// 如果選擇了客戶型別並且分配了客戶,則需要校驗客戶ID是否存在
if(!CommonUtil.checkStringIsNullOrLine(cellList.get(7)) && !CommonUtil.checkStringIsNullOrLine(cellList.get(8))) {
// 校驗客戶ID是否存在
Boolean hasCustomerIdFlag = true;
if(CollectionUtils.isEmpty(customerIdList)) {
hasCustomerIdFlag = false;
legalFlag = false;
} else {
if(!customerIdList.contains(cellList.get(8).trim())) {
hasCustomerIdFlag = false;
legalFlag = false;
}
}
// 如果該客戶ID存在,並且選中的客戶型別是VBOSS,則需要校驗賬戶和客戶是否匹配
if(hasCustomerIdFlag) {
if(cellList.get(7).equals("VBOSS") && !CommonUtil.checkStringIsNullOrLine(cellList.get(9))) {
if(CollectionUtils.isEmpty(customerVOList)) {
legalFlag = false;
} else {
for (CustomerVO eachCustomerVO: customerVOList) {
if(eachCustomerVO.getCustomerId().equals(cellList.get(8).trim())) {
accountIdList = eachCustomerVO.getAccountIdList();
break;
}
}
if(CollectionUtils.isEmpty(accountIdList)) {
legalFlag = false;
} else {
if(!accountIdList.contains(cellList.get(9).trim())) {
legalFlag = false;
}
}
}
}
}
}
// 如果資料合法,則批量儲存號碼物件
if (!legalFlag) {
if (!errorRowNumber.contains(curRow)) {
errorRowNumber.add(curRow);
}
} else {
try {
MobileManager mobileManager = new MobileManager();
mobileManager.setMobile(cellList.get(0).trim());
mobileManager.setAreaCode(CommonUtil.checkStringIsNullOrLine(cellList.get(1)) ? null : cellList.get(1));
mobileManager.setProvinceId(provinceId == null ? null : provinceId);
mobileManager.setCityId(cityId == null ? null : cityId);
mobileManager.setType(CommonUtil.checkStringIsNullOrLine(cellList.get(4)) ? null : cellList.get(4));
if (!CommonUtil.checkStringIsNullOrLine(cellList.get(5))) {
Integer operator = null;
if (cellList.get(5).trim().equals("移動")) {
operator = MobileManagerConstant.OPERATOR_YIDONG;
} else if (cellList.get(5).trim().equals("聯通")) {
operator = MobileManagerConstant.OPERATOR_LIANTONG;
} else if (cellList.get(5).trim().equals("電信")) {
operator = MobileManagerConstant.OPERATOR_DIANXIN;
} else if (cellList.get(5).trim().equals("鐵通")) {
operator = MobileManagerConstant.OPERATOR_TIETONG;
} else if (cellList.get(5).trim().equals("其它")) {
operator = MobileManagerConstant.OPERATOR_ELSE;
}
mobileManager.setOperator(operator);
}
mobileManager.setSupplierName(CommonUtil.checkStringIsNullOrLine(cellList.get(6)) ? null : cellList.get(6));
if (!CommonUtil.checkStringIsNullOrLine(cellList.get(7))) {
Integer customerType = null;
if (cellList.get(7).trim().equalsIgnoreCase("VBOSS")) {
customerType = MobileManagerConstant.CUSTOMER_TYPE_VBOSS;
mobileManager.setAccountId(CommonUtil.checkStringIsNullOrLine(cellList.get(9)) ? null : cellList.get(9));
} else if (cellList.get(7).trim().equalsIgnoreCase("BSS")) {
customerType = MobileManagerConstant.CUSTOMER_TYPE_BSS;
}
mobileManager.setCustomerType(customerType);
mobileManager.setCustomerId(CommonUtil.checkStringIsNullOrLine(cellList.get(8)) ? null : cellList.get(8));
mobileManager.setState(CommonUtil.checkStringIsNullOrLine(cellList.get(8)) ?
MobileManagerConstant.STATE_USEING : MobileManagerConstant.STATE_USEING);
} else {
mobileManager.setState(MobileManagerConstant.STATE_UNUSE);
}
mobileManager.setTerminalUser(CommonUtil.checkStringIsNullOrLine(cellList.get(10)) ? null : cellList.get(10));
if (!CommonUtil.checkStringIsNullOrLine(cellList.get(11))) {
mobileManager.setPlanRecoveryDate(DateUtil.formatDateStrWithLine2Date(cellList.get(11).trim()));
}
mobileManager.setCostMobileFee(CommonUtil.checkStringIsNullOrLine(cellList.get(12)) ? null : Double.parseDouble(cellList.get(12).trim()));
mobileManager.setCostLowFee(CommonUtil.checkStringIsNullOrLine(cellList.get(13)) ? null : Double.parseDouble(cellList.get(13).trim()));
mobileManager.setCustomerMobileFee(CommonUtil.checkStringIsNullOrLine(cellList.get(14)) ? null : Double.parseDouble(cellList.get(14).trim()));
mobileManager.setCustomerLowFee(CommonUtil.checkStringIsNullOrLine(cellList.get(15)) ? null : Double.parseDouble(cellList.get(15).trim()));
mobileManager.setRemark(CommonUtil.checkStringIsNullOrLine(cellList.get(16)) ? null : cellList.get(16).trim());
mobileManager.setCreateTime(new Date());
mobileManagerList.add(mobileManager);
if (mobileManagerList.size() == ExcelConstant.PER_READ_INSERT_BATCH_COUNT) {
mobileManagerMapper.saveMobileManagerBatch(mobileManagerList);
mobileManagerList.clear();
} else if (mobileManagerList.size() < ExcelConstant.PER_READ_INSERT_BATCH_COUNT) {
int lastInsertBatchCount = totalRowCount % ExcelConstant.PER_READ_INSERT_BATCH_COUNT == 0 ?
totalRowCount / ExcelConstant.PER_READ_INSERT_BATCH_COUNT :
totalRowCount / ExcelConstant.PER_READ_INSERT_BATCH_COUNT + 1;
if ((curRow - 1) >= ((lastInsertBatchCount - 1) * ExcelConstant.PER_READ_INSERT_BATCH_COUNT + 1)
&& (curRow - 1) < lastInsertBatchCount * ExcelConstant.PER_READ_INSERT_BATCH_COUNT) {
if (curRow - 1 == totalRowCount) {
mobileManagerMapper.saveMobileManagerBatch(mobileManagerList);
}
}
}
} catch (Exception e) {
e.printStackTrace();
if (!errorRowNumber.contains(curRow)) {
errorRowNumber.add(curRow);
}
}
}
}
});
logger.info("匯入號碼列表完成:" + DateUtil.formatDate(new Date(), DateUtil.YYYY_MM_DD_HH_MM_SS));
return ResultVO.getSuccess("批量匯入VOIP使用者完成, 共有" + errorRowNumber.size() + "條記錄存在問題,失敗行號為:" + errorRowNumber);
}