引言
在日常開(kāi)發(fā)過(guò)程中呻拌,Excel 導(dǎo)入是非常常見(jiàn)的場(chǎng)景葱轩,而且也有很多開(kāi)源的項(xiàng)目是針對(duì)Excel的讀寫的,如Apache 的poi 藐握,最近用的比較好的還是阿里的EasyExcel 開(kāi)源工具靴拱。平時(shí)我們只是簡(jiǎn)單的讀取文件并寫入數(shù)據(jù)庫(kù)持久化即可,但是前段時(shí)間猾普,產(chǎn)品搞了個(gè)需求袜炕,需要將導(dǎo)入失敗的數(shù)據(jù)及原因?qū)懭隕xcel并下載,那這就有得玩了初家,廢話不多說(shuō)偎窘,上才藝。
產(chǎn)品需求
- 導(dǎo)入Excel數(shù)據(jù)
- 數(shù)據(jù)格式校驗(yàn)
- 數(shù)據(jù)合法性校驗(yàn)(校驗(yàn)數(shù)據(jù)庫(kù))
- 失敗數(shù)據(jù)提供用戶下載溜在,并支持再次導(dǎo)入
技術(shù)選型
- 陌知,Excel 讀取/寫入
- ,做異步處理
需求實(shí)現(xiàn)
項(xiàng)目依賴(maven)
<!-- easyexcle -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcle</artifactId>
<version>2.2.6</version>
</dependency>
<!-- xxl job -->
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version>${xxl-job.version}</version>
</dependency>
文件解析
解析導(dǎo)入文件掖肋,獲取文件數(shù)據(jù)量仆葡,用于判定導(dǎo)入是否走異步導(dǎo)入。
public class EasyExcelUtils {
/**
*
* 解析文件培遵,獲取最后一行
* @param inputStream 文件流
* @param sheetNum 讀取excel表格的sheetNum 索引
* @return 總行數(shù)
*/
public static Integer lastNum(InputStream inputStream,Integer sheetNum){
Workbook wb = null;
sheetNum = sheetNum == null ? 0 : sheetNum;
try {
wb = WorkbookFactory.create(inputStream);
Sheet sheet = wb.getSheetAt(sheetNum);
CellReference cellReference = new CellReference("A4");
// 處理空行
for (int i = cellReference.getRow();i <= sheet.getLastRowNum();){
// 省略部分代碼
}
return sheet.getLastRowNum();
} catch (Exception e){
}
return 0;
}
}
判定導(dǎo)入數(shù)據(jù)文件是否為空浙芙,如果為空登刺,將返回錯(cuò)誤信息
@RestController
// 省略其他注解
public class ProjectInfoController {
/**
* 項(xiàng)目信息導(dǎo)入
*/
@PostMapping("/import")
public R projectInfoImport(MultipartFile file,HttpServletResponse response){
InputStream inputStream = null;
int lastNum = 0;
try {
lastNum = EasyExcelUtils.lastNum(file.getInputStream());
}catch(IOException e){
// 省略部分代碼
}
if (lastNum <= 0 ){
throw CustomExcetpoin(500,"導(dǎo)入文件數(shù)據(jù)為空,請(qǐng)重新上傳");
}
}
}
文件解析拿到導(dǎo)入數(shù)據(jù)的數(shù)據(jù)量嗡呼,與系統(tǒng)配置的文件導(dǎo)入上限值進(jìn)行判定纸俭,如果大于上限值將走異步處理(異步導(dǎo)入,請(qǐng)查看異步“異步導(dǎo)入”導(dǎo)入內(nèi)容)南窗。
@RestController
// 省略其他注解
public class ProjectInfoController {
@Resource
private AsyncExcelService asyncExcelService;
/**
* 項(xiàng)目信息導(dǎo)入
*/
@PostMapping("/import")
public R projectInfoImport(MultipartFile file,HttpServletResponse response){
InputStream inputStream = null;
int lastNum = 0;
try {
lastNum = EasyExcelUtils.lastNum(file.getInputStream());
}catch(IOException e){
// 省略部分代碼
}
if (lastNum <= 0 ){
throw CustomExcetpoin(500,"導(dǎo)入文件數(shù)據(jù)為空揍很,請(qǐng)重新上傳");
}
// 獲取系統(tǒng)配置的導(dǎo)入上限值
Integer importMax = asyncExcelService.asyncProjectImportMax();
if (lastNum > importMax ){
// 達(dá)到上限,走異步
asyncExcelService.asyncProjectImport(file,response);
return R.success("數(shù)據(jù)導(dǎo)入成功万伤,因數(shù)據(jù)量比較大窒悔,已轉(zhuǎn)為異步導(dǎo)入");
}
// 省略其他代碼
}
}
AsyncExcelService 接口實(shí)現(xiàn)
/**
* 異步導(dǎo)出/導(dǎo)入 service
*/
public interface AsyncExcelService {
/** 默認(rèn)導(dǎo)入數(shù)據(jù)上限 **/
Integer DEFAULT_IMPORT_DATA_MAX = 500;
/**
* 獲取最大導(dǎo)入上限值,超過(guò)則走異步
*/
Integer getImportMax();
/**
* 異步導(dǎo)入數(shù)據(jù)
*/
void asyncProjectImport(MultipartFile file,HttpServletResponse response);
}
@Service
// 省略其他注解
public class AsyncExcelServiceImpl implements AsyncExcelService {
@Resource
private IParamtersClient paramtersClient;
@Override
public Integer getImportMax(){
Integer value = getParamVaule("paramName",Integer.class);
return value == null ? DEFAULT_IMPORT_DATA_MAX : value;
}
/**
* 調(diào)用框架接口獲取系統(tǒng)參數(shù)
*
*/
private <T> T getParamVaule(String name,Class<T> clazz){
CCBHousingUser user = SecureUtil.getUser();
// 省略部分代碼
// 獲取系統(tǒng)配置參數(shù)
Parameters parameters = paramtersClient.getParamterByCodeAndOrg(name,user.getOrganizationId());
// 省略部分代碼
}
}
其中敌买,IParamtersClient 屬于框架提供的feign 接口简珠,也可以根據(jù)自己的實(shí)際場(chǎng)景實(shí)現(xiàn)相關(guān)邏輯。
數(shù)據(jù)合法校驗(yàn)
導(dǎo)入數(shù)據(jù)文件解析使用的是alibaba 提供的 EasyExcel 開(kāi)源工具虹钮,我們需要在 EasyExcel 工具的基礎(chǔ)上做一些增強(qiáng)處理聋庵,如:導(dǎo)入格式校驗(yàn)、導(dǎo)入表頭校驗(yàn)芙粱、導(dǎo)入數(shù)據(jù)格式校驗(yàn)等祭玉,如果發(fā)生校驗(yàn)失敗,將錯(cuò)誤信息寫入錯(cuò)誤報(bào)告(excel)輸出到客戶端春畔。
定義easyexcel 導(dǎo)入文件到列與實(shí)體映射關(guān)系脱货,將使用到 easyexcel 到@ExcleProperty 注解進(jìn)行關(guān)系綁定
@Data
// 省略其他注解
public class ProjectInfoExcelDTO {
@ExcelProperty(index=0,value="序列號(hào)")
private String number;
@ExcelProperty(index=1,value="項(xiàng)目名稱")
private String name;
// 省略其他字段屬性
}
注解 @ExcleProperty 常用屬性
- index,與excel文件中律姨,表頭列的索引位置對(duì)應(yīng)(從0開(kāi)始)
- value振峻,與excel文件中,表頭列的名稱相對(duì)應(yīng)
- converter,指定解析數(shù)據(jù)時(shí)线召,該列需要使用的數(shù)據(jù)轉(zhuǎn)換器铺韧,轉(zhuǎn)換器實(shí)現(xiàn)Converter接口
定義校驗(yàn)錯(cuò)誤的數(shù)據(jù)結(jié)構(gòu)類型
@Data
// 省略其他注解
public class ExcelChcekErrDTO<T> {
private T t;
private String errMsg;
}
備注:@Data 屬于 lombok 工具,簡(jiǎn)化Bean的封裝缓淹,感興趣的同學(xué),可以自行查閱資料塔逃。
定義Excel導(dǎo)入校驗(yàn)返回的數(shù)據(jù)VO
@Data
// 省略其他注解
public class ExcelCheckResultVO<T> {
/** 校驗(yàn)成功的數(shù)據(jù) **/
private List<T> successDatas;
/** 校驗(yàn)失敗的數(shù)據(jù) **/
private List<ExcelChcekErrDTO> errData;
}
定義數(shù)據(jù)解析監(jiān)聽(tīng)器EasyExcelListener
@Data
// 省略部分注解
public class EasyExcelListener<T> extends AnalysisEventListener<T> {
// 省略部分代碼
}
定義excel 業(yè)務(wù)校驗(yàn)管理器 ExcelCheckManager,需要做業(yè)務(wù)校驗(yàn)的(與數(shù)據(jù)庫(kù)匹配等)需要實(shí)現(xiàn)該接口
public interface ExcelCheckManager<T> {
ExcelCheckResultVO checkImportExcle(List<T> datas);
}
表頭校驗(yàn)
使用EasyExcelListener 用來(lái)監(jiān)聽(tīng)數(shù)據(jù)解析過(guò)程讯壶,其中,invokHeadMap 方法將在解析完成excel表頭時(shí)將被執(zhí)行
@Data
// 省略部分注解
public class EasyExcelListener<T> extends AnalysisEventListener<T> {
/** excel 對(duì)象的反射類 **/
private Class<T> clazz;
private ExcelCheckManager<T> excelCheckManager;
public EasyExcelListener(ExcelCheckManager<T> excelCheckManager,Class<T> clazz){
this.clazz = clazz;
this.excelCheckManager = excelCheckManager;
}
@Override
public void invokHeadMap(Map<Integer,String> headMap,AnalysisContext context){
super.invokHeadMap(headMap,context);
// 反射獲取實(shí)體到屬性值
Map<Integer,String> indexNameMap = getIndexNameMap(clazz);
// 將 headMap 與 indexNameMap 進(jìn)行對(duì)比湾盗,是否完全匹配
Set<Integer> keySet = indexNameMap.keySet();
for (Integer key : keySet ){
if (StringUtils.isEmpty(headMap.get(key)){
throw ExcelAnalysisExcetpion("數(shù)據(jù)解析錯(cuò)誤伏蚊,請(qǐng)傳入正確的excel格式");
}
if (!headMap.get(key).equals(indexNameMap.get(key)){
throw ExcelAnalysisExcetpion("數(shù)據(jù)解析錯(cuò)誤,請(qǐng)傳入正確的excel格式");
}
}
}
/**
* 反射獲取解析數(shù)據(jù)實(shí)體的@ExcleProperty 的value
*/
public Map<Integer,String> getIndexNameMap(Class clazz){
Map<Integer,String> result = new HashMap<>();
Field field;
Field[] fields = clazz.getDeclaredFields();
for (int i = 0; i < fields.length; i++){
field = clazz.getDeclaredField(fields[i].getName());
field.setAccessible(true);
ExcelProperty excleProperty = field.getAnnotation(ExcelProperty.class);
if (excelProperty != null){
int index = excleProperty.index();
String[] values = excleProperty.value();
StringBuilder value = new StringBuilder();
for (String v : values ){
value.append(v);
}
result.put(index,value.toString());
}
}
return result;
}
}
數(shù)據(jù)非空格粪、格式校驗(yàn)
數(shù)據(jù)非空校驗(yàn)躏吊、格式校驗(yàn)氛改,我們將使用hibernate-validator 校驗(yàn)器進(jìn)行校驗(yàn)格式。
定義validator 工具類
@component
public class EasyExcelValidatorHelper {
private static Validtor validtor;
@Autowired
public EasyExcelValidatorHelper(Validtor validtor){
this.EasyExcelValidatroHelper.validtor = validtor;
}
public static <T> String validateEntity(T obj) throws NoSuchFieldException{
StringBuilder result = new StringBuilder();
// 執(zhí)行校驗(yàn)
Set<ConstraionViolation<T>> set = validtor.validate(obj,Default.class);
// 組裝結(jié)果
if(set != null && !set.isEmpty()){
for (ConstraionViolation<T> cv : set ){
Field declaredField = obj.getClass.getDeclaredField(cv.getPropertiyPath().toString());
ExcelProperty annotation = declaredField.getAnnotation(ExcelProperty.class);
result.append(annotation.value[0]+":"+cv.getMessage()).append(";");
}
}
return result;
}
}
數(shù)據(jù)格式校驗(yàn)比伏,使用EasyExcelListener 用來(lái)監(jiān)聽(tīng)數(shù)據(jù)解析過(guò)程胜卤,其中,invok 方法將逐行解析excel數(shù)據(jù)的時(shí)候?qū)⒈徽{(diào)用
@Data
// 省略部分注解
public class EasyExcelListener<T> extends AnalysisEventListener<T> {
/** 標(biāo)記是否執(zhí)行數(shù)據(jù)解析 **/
private boolean baseMatching = false;
/** 解析成功的數(shù)據(jù) **/
private List<T> successList = new ArrayList<>();
/** 解析失敗的數(shù)據(jù) **/
private List<ExcelCheckErrDTO<T>> errList = new ArrayList<>();
/** excel 對(duì)象的反射類 **/
private Class<T> clazz;
private List<T> list;
private ExcelCheckManager<T> excelCheckManager;
public EasyExcelListener(ExcelCheckManager<T> excelCheckManager,Class<T> clazz){
this.clazz = clazz;
this.excelCheckManager = excelCheckManager;
}
@Override
public void invok(T t,AnalysisContext context){
// 數(shù)據(jù)解析/轉(zhuǎn)換完成赁项,標(biāo)記進(jìn)入到解析起
baseMatching = true;
String errMsg;
try {
// 調(diào)用驗(yàn)證器驗(yàn)證數(shù)據(jù)格式
errMsg = EasyExcelValidatorHelper.validateEntity(t);
}catch(Exception e){
errMsg = "解析數(shù)據(jù)出錯(cuò)";
// 省略部分代碼
}
// 校驗(yàn)不通過(guò)
if (!StringUtils.isEmpty(errMsg){
// 將錯(cuò)誤數(shù)據(jù)放入錯(cuò)誤列表中
ExcelChcekErrDTO errDTO = new ExcelChcekErrDTO(t,errMsg);
errList.add(errDTO);
} else{
// 校驗(yàn)成功
list.add(t);
}
if (list.size() > 1000){
// 業(yè)務(wù)校驗(yàn)
ExcelCheckResultVO excelCheckResultVO = excelCheckManager.checkImportExcel(list);
successList.addAll(excelCheckResultVO.getSuccessDatas());
errList.addAll(excelCheckResultVO.getErrDatas());
list.clear();
}
}
/**
* 所有數(shù)據(jù)解析完成后調(diào)用此方法
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context){
ExcelCheckResultVO excelCheckResultVO = excelCheckManager.checkImportExcel(list);
successList.addAll(excelCheckResultVO.getSuccessDatas());
errList.addAll(excelCheckResultVO.getErrDatas());
list.clear();
}
@Override
public void invokHeadMap(Map<Integer,String> headMap,AnalysisContext context){
super.invokHeadMap(headMap,context);
// 反射獲取實(shí)體到屬性值
Map<Integer,String> indexNameMap = getIndexNameMap(clazz);
// 將 headMap 與 indexNameMap 進(jìn)行對(duì)比葛躏,是否完全匹配
Set<Integer> keySet = indexNameMap.keySet();
for (Integer key : keySet ){
if (StringUtils.isEmpty(headMap.get(key)){
throw ExcelAnalysisExcetpion("數(shù)據(jù)解析錯(cuò)誤,請(qǐng)傳入正確的excel格式");
}
if(!headMap.get(key).equals(indexNameMap.get(key)){
throw ExcelAnalysisExcetpion("數(shù)據(jù)解析錯(cuò)誤悠菜,請(qǐng)傳入正確的excel格式");
}
}
}
/**
* 反射獲取解析數(shù)據(jù)實(shí)體的@ExcleProperty 的value
*/
public Map<Integer,String> getIndexNameMap(Class clazz){
Map<Integer,String> result = new HashMap<>();
Field field;
Field[] fields = clazz.getDeclaredFields();
for (int i = 0; i < fields.length; i++){
field = clazz.getDeclaredField(fields[i].getName());
field.setAccessible(true);
ExcelProperty excleProperty = field.getAnnotation(ExcelProperty.class);
if (excelProperty != null){
int index = excleProperty.index();
String[] values = excleProperty.value();
StringBuilder value = new StringBuilder();
for (String v : values ){
value.append(v);
}
result.put(index,value.toString());
}
}
return result;
}
}
對(duì)需要進(jìn)行校驗(yàn)對(duì)字段添加注解
@Data
// 省略其他注解
public class ProjectInfoExcelDTO {
@ExcelProperty(index=0,value="序列號(hào)")
private String number;
@ExcelProperty(index=1,value="項(xiàng)目名稱")
@NotBlank(message = "請(qǐng)?zhí)顚戫?xiàng)目名稱")
private String name;
// 省略其他字段屬性
}
validator 常用注解傳送門(validator 常用注解)舰攒。
EasyExcel 讀取數(shù)據(jù),并調(diào)用格式校驗(yàn)
@RestController
// 省略其他注解
public class ProjectInfoController {
@Resource
private AsyncExcelService asyncExcelService;
@Resource
private ProjectInfoService projectInfoService;
/**
* 項(xiàng)目信息導(dǎo)入
*/
@PostMapping("/import")
public R projectInfoImport(MultipartFile file,HttpServletResponse response){
InputStream inputStream = null;
int lastNum = 0;
try {
lastNum = EasyExcelUtils.lastNum(file.getInputStream());
}catch(IOException e){
// 省略部分代碼
}
if (lastNum <= 0 ){
throw CustomExcetpoin(500,"導(dǎo)入文件數(shù)據(jù)為空悔醋,請(qǐng)重新上傳");
}
// 獲取系統(tǒng)配置的導(dǎo)入上限值
Integer importMax = asyncExcelService.asyncProjectImportMax();
if (lastNum > importMax ){
// 達(dá)到上限摩窃,走異步
asyncExcelService.asyncProjectImport(file,response);
return R.success("數(shù)據(jù)導(dǎo)入成功,因數(shù)據(jù)量比較大芬骄,已轉(zhuǎn)為異步導(dǎo)入");
}
// 省略部分代碼
// 實(shí)例數(shù)據(jù)解析監(jiān)聽(tīng)器
EasyExcelListener<ProjectInfoDTO> easyExcleListener = new EasyExcelListener(projectInfoService,ProjectInfoDTO.class);
// 文件讀取/解析偶芍,并注冊(cè)監(jiān)聽(tīng)器
EasyExcle.read(file.getInputStream(),ProjectInfoDTO.class,easyExcleListener).sheet(1).doRead();
// 獲取錯(cuò)誤數(shù)據(jù)
List<ExcelCheckErrDTO<ProjectInfoExcelDTO>> errList = easyExcleListener.getErrList();
// 獲取解析成功到數(shù)據(jù)
List<ProjectinfoExcelDTO> successList = easyExcleListener.getSuccessList();
// 如果錯(cuò)誤數(shù)據(jù)不為空,將錯(cuò)誤數(shù)據(jù)寫入到excel文件德玫,并輸出到瀏覽器
// 省略代碼
// 將成功到數(shù)據(jù)匪蟀,批量寫入到數(shù)據(jù)庫(kù)中
// 省略代碼
// 省略其他代碼
}
}
ProjectInfoService 聲明與實(shí)現(xiàn),因?yàn)樾枰鰳I(yè)務(wù)數(shù)據(jù)到校驗(yàn)宰僧,因此ProjectInfoService 需要繼承 ExcelCheckManager 驗(yàn)證管理器
public interface ProjectInfoService extends ExcelCheckManager{
}
@Service
// 省略其他注解
public class ProjectInfoServiceImpl implements ProjectInfoService {
// 省略部分代碼
@Override
public ExcelCheckResultVO checkImportExcel(List<ProjectInfoExcelDTO> datas){
// 省略代碼
}
}
輸出錯(cuò)誤報(bào)告
文件校驗(yàn)完成之后材彪,如果沒(méi)有完全通過(guò),需要將錯(cuò)誤對(duì)數(shù)據(jù)以及錯(cuò)誤信息通過(guò)easyExcel 輸出到客戶端琴儿。
@RestController
// 省略其他注解
public class ProjectInfoController {
@Resource
private AsyncExcelService asyncExcelService;
@Resource
private ProjectInfoService projectInfoService;
/**
* 項(xiàng)目信息導(dǎo)入
*/
@PostMapping("/import")
public R projectInfoImport(MultipartFile file,HttpServletResponse response){
InputStream inputStream = null;
int lastNum = 0;
try {
lastNum = EasyExcelUtils.lastNum(file.getInputStream());
}catch(IOException e){
// 省略部分代碼
}
if (lastNum <= 0 ){
throw CustomExcetpoin(500,"導(dǎo)入文件數(shù)據(jù)為空段化,請(qǐng)重新上傳");
}
// 獲取系統(tǒng)配置的導(dǎo)入上限值
Integer importMax = asyncExcelService.asyncProjectImportMax();
if (lastNum > importMax ){
// 達(dá)到上限,走異步
asyncExcelService.asyncProjectImport(file,response);
return R.success("數(shù)據(jù)導(dǎo)入成功造成,因數(shù)據(jù)量比較大显熏,已轉(zhuǎn)為異步導(dǎo)入");
}
// 省略部分代碼
// 實(shí)例數(shù)據(jù)解析監(jiān)聽(tīng)器
EasyExcelListener<ProjectInfoDTO> easyExcleListener = new EasyExcelListener(projectInfoService,ProjectInfoDTO.class);
// 文件讀取/解析,并注冊(cè)監(jiān)聽(tīng)器
EasyExcle.read(file.getInputStream(),ProjectInfoDTO.class,easyExcleListener).sheet(1).doRead();
// 獲取錯(cuò)誤數(shù)據(jù)
List<ExcelCheckErrDTO<ProjectInfoExcelDTO>> errList = easyExcleListener.getErrList();
// 獲取解析成功到數(shù)據(jù)
List<ProjectinfoExcelDTO> successList = easyExcleListener.getSuccessList();
// 如果錯(cuò)誤數(shù)據(jù)不為空晒屎,將錯(cuò)誤數(shù)據(jù)寫入到excel文件喘蟆,并輸出到瀏覽器
if (errList.size() > 0 ){
// 省略部分代碼
}
// 將成功到數(shù)據(jù),批量寫入到數(shù)據(jù)庫(kù)中
// 省略代碼
// 省略其他代碼
}
}
異步導(dǎo)入
異步導(dǎo)入操作鼓鲁,將思考幾個(gè)問(wèn)題:
- 導(dǎo)入文件存到什么地方蕴轨?當(dāng)一個(gè)同步請(qǐng)求結(jié)束之后,后續(xù)我們想再次拿到該請(qǐng)求到數(shù)據(jù)骇吭,我們應(yīng)該考慮將文件放到某一個(gè)單獨(dú)到地方橙弱,提供我們二次使用,比如:自己到文件服務(wù)器、oss 存儲(chǔ)等棘脐,這里我們使用自己的文件服務(wù)器斜筐。
- 怎么異步執(zhí)行?我們可以使用新啟用一個(gè)本地線程去執(zhí)行我們的操作蛀缝,不影響當(dāng)前請(qǐng)求主線程的操作顷链,也是可以的,但是考慮到執(zhí)行重試問(wèn)題内斯,我們將使用(#xxl-job)分布式調(diào)度系統(tǒng)蕴潦,進(jìn)行調(diào)度執(zhí)行任務(wù)。
- 客戶如何查看任務(wù)執(zhí)行狀態(tài)俘闯?我們需要提供一個(gè)任務(wù)執(zhí)行日志列表潭苞,讓用戶可以清晰的看到本次導(dǎo)出的任務(wù)是否執(zhí)行完成/是否存在導(dǎo)入錯(cuò)誤。
- 怎么將錯(cuò)誤報(bào)告輸出給到客戶真朗?我們需要將導(dǎo)入到錯(cuò)誤報(bào)告文件(excel)上傳至文件服務(wù)器此疹,提供用戶二次或多次下載使用;同時(shí)遮婶,需要將文件信息保存至任務(wù)執(zhí)行日志信息中蝗碎,為用戶提供下載入口。
定義通用的job handler 父類 AsyncTaskHandler 旗扑,所有需要使用xxl-job 發(fā)起異步任務(wù)和給xxl-job 發(fā)起回調(diào)蹦骑,都需要繼承AsyncTaskHandler ,并實(shí)現(xiàn)execute 抽象方法臀防。
public abstract class AsyncTaskHandler <T extends AsyncTaskPramsDTO> {
/** xxl-job server 端提供的創(chuàng)建任務(wù)接口 uri **/
private final static String JOB_ADMIN_URI = "/outapi/asyn/";
/** 與xxl-job server 通訊的加密密鑰對(duì) **/
@Setter
protected String publicKey;
/**
* xxl-job server 回調(diào)對(duì)方法
*/
public abstract ReturnT<String> execute(String params);
/**
* 向xxl-job 發(fā)起調(diào)度任務(wù)
*/
public JobResponseDTO sendTask(T prams){
prams.setUser(null);
// 省略部分代碼眠菇,相關(guān)內(nèi)容,請(qǐng)查詢xxl-job server 端所提供的接口文檔
// 將 params 中的 user 對(duì)象保存至redis 中袱衷,xxl-job 接口有長(zhǎng)度限制
}
public abstract RedisUtil getRedisUtil();
public abstract JobProperties getJobProperties();
/** 回調(diào)方法名稱 **/
public abstract String getHandlerName();
}
定義 AsyncTaskPramsDTO 異步參數(shù)實(shí)體
@Data
// 省略其他注解
public class AsyncTaskPramsDTO {
private String requestId;
}
數(shù)據(jù)導(dǎo)出
數(shù)據(jù)導(dǎo)出功能常指捎废,客戶想將系統(tǒng)中的相關(guān)(按照查詢條件篩選)數(shù)據(jù)通過(guò)excel形式保存到自己本地。在數(shù)據(jù)導(dǎo)出過(guò)程中致燥,需要通過(guò)數(shù)據(jù)篩選條件將數(shù)據(jù)從系統(tǒng)數(shù)據(jù)庫(kù)中篩選出來(lái)登疗,然后通過(guò)一定格式(excel導(dǎo)出模版格式)寫入到excel中,最后輸出到客戶端(瀏覽器)提供客戶下載保存到本地嫌蚤。