前言:由于多年在写的都是python,经常在做一些异步任务时,如导入导出报表这种,都是用celery来做异步生成表格,然后循环更新任务状态,任务结束后返回文件名或其他结果。最近某个项目采用java的springboot在写,刚好也要做些报表导入导出的事情,由此查了一下资料,捡了一些别人写好的东西,并撸了一遍车轮子,以下是相关工程代码,在此记录,下次遇到好直接搬运。

1.pom文件,在此强烈推荐阿里巴巴的easyexcel,真的好用,官网文档:https://www.yuque.com/easyexcel/doc/easyexcel

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.4</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>2.1.4</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

贴个目录结构

 2.开启异步任务支持和配置

SpringTaskExecutor.java

package com.src.xxx;

import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;

@Configuration
@EnableAsync   //开启异步任务支持
public class SpringTaskExecutor implements AsyncConfigurer {
    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setCorePoolSize(5);
        taskExecutor.setMaxPoolSize(10);
        taskExecutor.setQueueCapacity(20);
        taskExecutor.initialize();
        return taskExecutor;
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return null;
    }
}

AsyncTaskConstructor.java

package com.src.xxx.task;

public interface AsyncTaskConstructor {
    public void async();
}

AsyncTaskExecutor.java

package com.src.xxx.task;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
@Component
public class AsyncTaskExecutor {
    private static Logger LOG = LoggerFactory.getLogger(AsyncTaskExecutor.class);
    @Async
    public void executor(AsyncTaskConstructor asyncTaskGenerator, String taskInfo) {
        LOG.info("AsyncTaskExecutor is executing async taskController:{}", taskInfo);
        asyncTaskGenerator.async();
    }
}

AsyncTaskManager.java 

package com.src.xxx.task;
import com.src.xxx.entity.Task;
import com.src.xxx.service.taskService;
import com.src.xxx.tools.DatePattern;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

/**
 * 异步任务管理器
 */
@Component
public class AsyncTaskManager {
    private final taskService taskService;
    public AsyncTaskManager(taskService taskService) {
        this.taskService = taskService;
    }

    @Autowired
    AsyncTaskExecutor asyncTaskExecutor;




    /**
     * 初始化任务
     * @param asyncTaskConstructor 异步任务构造器
     * @return taskInfo
     */
    public Task submit(AsyncTaskConstructor asyncTaskConstructor, String taskId) {
        Task task = taskService.get_task(taskId);
        if(task == null){
            return null;
        }
        asyncTaskExecutor.executor(asyncTaskConstructor,taskId);
        return task;
    }

    /**
     * 保存任务信息
     */
    public void setTaskInfo(Task task) {
        taskService.update_task(task);
    }

    /**
     * 获取任务信息
     *
     * @param taskId 任务ID
     * @return
     */
    public Task getTaskInfo(String taskId) {
        return taskService.get_task(taskId);
    }

}

AsyncTaskMonitor.java

package com.src.xxx.task;

import com.src.xxx.entity.Task;
import com.src.xxx.tools.DatePattern;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Date;

/**
 * 异步任务监控
 */
@Component
@Aspect
public class AsyncTaskMonitor {
    @Autowired
    AsyncTaskManager manager;
    private static Logger LOG = LoggerFactory.getLogger(AsyncTaskMonitor.class);

    @Around("execution(* com.src.hospotal.task.AsyncTaskExecutor.*(..))")
    public void taskHandle(ProceedingJoinPoint pjp) {
        //获取taskId
        String taskId = pjp.getArgs()[1].toString();
        //获取任务信息
        LOG.info("AsyncTaskMonitor is monitoring async taskController:{}", taskId);
        try {
            pjp.proceed();
        } catch (Throwable throwable) {
            Task task = manager.getTaskInfo(taskId);
            task.setStatus(1);
            task.setProgress(100);
            task.setMsg("异步任务出现错误!");
            task.setInfo(throwable.getMessage());
            task.setUpdatetime(DatePattern.getnow());
            manager.setTaskInfo(task);
            LOG.error("AsyncTaskMonitor:async taskController {} is failed.Error info:{}", taskId, throwable.getMessage());
        }
    }
}

上面目录结构里面的代码就完了。

接下来是上面代码依赖的一些其他包的文件

entity里面的Task.java

package com.src.xxx.entity;

import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiParam;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.format.annotation.DateTimeFormat;

import java.util.Date;

@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel
public class Task {
    //id,task_id,status,msg,info,progress,createtime
    //@ExcelProperty 这个注解表示给表头命名,不加这个注解的,会默认已字段名
    //@ExcelIgnore 这个注解表示,不导出该字段
    @ExcelProperty("记录id")
    @ApiModelProperty(value = "id")
    private Integer id;
    @ApiModelProperty(value = "任务ID")
    private String taskId;
    @ApiModelProperty(value = "0 进行中 1出错了 2已完成")
    private Integer status;
    @ApiModelProperty(value = "提示信息")
    private String msg;
    @ApiModelProperty(value = "返回结果原始数据,如果数据类型是json,可以取obj或自行")
    private String info;
    private Integer infoType;
    private Object obj;
    private Integer progress;
    @ApiParam(hidden = true)
    @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
    private Date createtime;
    private String updatetime;
    private Integer createuserid;
}

对应的数据库表结构

CREATE TABLE `xxx`.`task`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `task_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '任务ID',
  `status` int(11) NOT NULL DEFAULT 0 COMMENT '0 进行中 1出错了 2已完成',
  `msg` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '提示信息',
  `info` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '任务返回对象信息',
  `progress` int(11) NOT NULL DEFAULT 0 COMMENT '进度 0到100',
  `info_type` int(11) NOT NULL DEFAULT 0 COMMENT '0普通字符串,1json字符串',
  `createtime` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `createuserid` int(11) NOT NULL DEFAULT 0 COMMENT '创建者id',
  `updatetime` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '最后修改时间',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = MyISAM CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

dao里面的taskMapper.java

package com.src.xxx.dao;

import com.src.xxx.entity.Task;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import org.springframework.stereotype.Repository;

import java.util.List;

@Mapper
@Repository
public interface taskMapper {
    //task_id,status,msg,info,progress,info_type,createuserid
    @Insert("insert into task(task_id,status,msg,info,info_type,progress,createuserid,updatetime) values (#{taskId},#{status},#{msg},#{info},#{infoType},#{progress},#{createuserid},#{updatetime})")
    boolean insert_task(Task task);
    @Update("update task set status = #{status},msg = #{msg},info= #{info},info_type= #{infoType},progress = #{progress},updatetime= #{updatetime} where id=#{id}")
    boolean update_task(Task task);
    @Select("select * from task where task_id=#{taskId}")
    Task get_task(String taskId);
    @Select("select * from task where createuserid=#{createuserid} limit #{page},#{limit}")
    List<Task> get_task_list(Integer createuserid, Integer page, Integer limit);
    @Select("select count(*) from task where createuserid=#{createuserid} limit #{page},#{limit}")
    Integer count(Integer createuserid, Integer page, Integer limit);
}

 service 

package com.src.xxx.service;

import com.src.xxx.entity.Pager;
import com.src.xxx.entity.Task;

public interface taskService {
    //添加任务
    boolean insert_task(Task task);
    //更新任务
    boolean update_task(Task task);
    //获取某个任务
    Task get_task(String taskId);
    //获取任务列表
    Pager<Task> get_task_list(Integer createuserid, Integer page, Integer limit);
    // 不带分页的获取任务列表,为了测试
    List<Task> get_task(Integer createuserid);
}

entity里面有个分页的 Pager.java

package com.src.xxx.entity;

import io.swagger.annotations.ApiModel;
import lombok.Data;

import java.util.List;

@Data
@ApiModel
public class Pager<T> {
    private int page;//分页起始页
    private int size;//每页记录数
    private List<T> rows;//返回的记录集合
    private long total;//总记录条数
}

 serviceImpl中 taskServiceImpl.java

package com.src.xxx.serviceImpl;

import com.src.xxx.dao.taskMapper;
import com.src.xxx.entity.Pager;
import com.src.xxx.entity.Task;
import com.src.xxx.service.taskService;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class taskServiceImpl implements taskService {
    private final taskMapper taskMapper;

    public taskServiceImpl(taskMapper taskMapper) {
        this.taskMapper = taskMapper;
    }

    @Override
    public boolean insert_task(Task task) {
        return taskMapper.insert_task(task);
    }

    @Override
    public boolean update_task(Task task) {
        return taskMapper.update_task(task);
    }

    @Override
    public Task get_task(String taskId) {
        return taskMapper.get_task(taskId);
    }

    @Override
    public Pager<Task> get_task_list(Integer createuserid, Integer page, Integer limit) {
        Integer pageStrat = page>1 ? page-1 :0;
        Pager<Task> pager = new Pager<Task>();
        List<Task> list = taskMapper.get_task_list(createuserid,pageStrat*limit,limit);
        pager.setRows(list);
        pager.setPage(page);
        pager.setSize(limit);
        pager.setTotal(taskMapper.count(createuserid,pageStrat*limit,limit));
        return pager;
    }
    @Override
    public List<Task> get_task_list2(Integer createuserid) {
        //为了测试
        return taskMapper.get_task_list(createuserid,1,999);
    }
}

tools里面有个DatePattern.java

package com.src.xxx.tools;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.regex.Pattern;

public class DatePattern {
    public static final Pattern REGEX_NORM = Pattern.compile("\\d{4}-\\d{1,2}-\\d{1,2}( \\d{1,2}:\\d{1,2}(:\\d{1,2})?)?");
    public static final String NORM_DATE_PATTERN = "yyyy-MM-dd";
    public static final String NORM_TIME_PATTERN = "HH:mm:ss";
    public static final String NORM_DATETIME_MINUTE_PATTERN = "yyyy-MM-dd HH:mm";
    public static final String NORM_DATETIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
    public static final String NORM_DATETIME_MS_PATTERN = "yyyy-MM-dd HH:mm:ss.SSS";
    public static final String CHINESE_DATE_PATTERN = "yyyy年MM月dd日";
    public static final String PURE_DATE_PATTERN = "yyyyMMdd";
    public static final String PURE_TIME_PATTERN = "HHmmss";
    public static final String PURE_DATETIME_PATTERN = "yyyyMMddHHmmss";
    public static final String PURE_DATETIME_MS_PATTERN = "yyyyMMddHHmmssSSS";
    public static final String HTTP_DATETIME_PATTERN = "EEE, dd MMM yyyy HH:mm:ss z";
    public static final String JDK_DATETIME_PATTERN = "EEE MMM dd HH:mm:ss zzz yyyy";
    public static final String UTC_PATTERN = "yyyy-MM-dd'T'HH:mm:ss'Z'";
    public static final String UTC_WITH_ZONE_OFFSET_PATTERN = "yyyy-MM-dd'T'HH:mm:ssZ";
    public static final String UTC_MS_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
    public static final String UTC_MS_WITH_ZONE_OFFSET_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";

    public DatePattern() {
    }
    public static String getnow(){
        Date dd=new Date();
        //格式化
        SimpleDateFormat sim=new SimpleDateFormat(NORM_DATETIME_PATTERN);
        return sim.format(dd);
    }

}

tools里面的有个ExcelUtil.java

package com.src.xxx.tools;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.src.hospotal.entity.Task;

import java.io.File;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import java.util.List;
public class ExcelUtil {
    /**
     * 相对路径,文件会生成在与项目平级的目录
     */
    private static final String EXCEL_SUFFIX = ".xls";

    private static String FILE_PATH(){
        String UPLOAD_PATH = "C:/upload/excel/";
        if(System.getProperty("os.name").toLowerCase().indexOf("linux")>=0){
            UPLOAD_PATH = "/upload/excel/";
        }
        //获取文件上传的根目录 C:\Users\wanghao/upload/img
        return  UPLOAD_PATH;
    }
    /**
     * 在指定位置生成excel文件
     *
     * @param fileName 文件路径
     * @param clazz    导出数据类型
     * @param list     导出数据
     * @param <T>      可以不加这个也行,但是会有警告,看起来不舒服
     */
    public static <T> void writeToExcel(String fileName, Class<T> clazz, List<T> list) {
        ExcelWriter excelWriter = EasyExcel.write(fileName).build();
        WriteSheet sheet = EasyExcel.writerSheet().head(clazz).build();
        excelWriter.write(list, sheet);
        excelWriter.finish();
    }

    /**
     * 生成excel文件
     *
     * @param fileName
     * @param clazz
     * @param list
     * @param <T>
     * @return
     */
    public static <T> File create_Excel(String fileName, Class<T> clazz, List<T> list) {
        System.out.println(generateFilePath());
        if (!generateFilePath()) {
            return null;
        }
        String fileFullName = FILE_PATH() + fileName + EXCEL_SUFFIX;
        //System.out.println(fileFullName);
        ExcelUtil.writeToExcel(fileFullName, clazz, list);
        return new File(fileFullName);
    }

    /**
     * 删除文件
     *
     * @param file
     * @return
     */
    public boolean deleteFile(File file) {
        if (file != null) {
            return file.delete();
        }
        return true;
    }

    private static boolean generateFilePath() {
        File path = new File(FILE_PATH());
        //System.out.println(path);
        if (path.exists()) {
            return true;
        } else {
            return path.mkdirs();
        }
    }

    public static void main(String[] args) {
        List<Task> list = new ArrayList<Task>();
        for (int i = 0; i < 10; i++) {
            Task data = new Task();
            data.setMsg("字符串" + i);
            data.setUpdatetime(new Date().toString());
            list.add(data);
        }
        System.out.println(list);
        File f = create_Excel("11", Task.class, list);
        System.out.println(f);
    }
}

控制器

taskController.java
package com.src.xxx.controller;

import com.src.xxx.entity.*;
import com.src.xxx.service.taskService;
import com.src.xxx.task.AsyncTaskManager;
import com.src.xxx.tools.CodeUtil;
import com.src.xxx.tools.DatePattern;
import com.src.xxx.tools.Result;
import com.src.xxx.tools.R;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.lang.System;
import java.util.Date;
import java.util.List;
import java.util.UUID;

import static com.src.hospotal.tools.ExcelUtil.create_Excel;

@RestController
@RequestMapping("/api/task")
@Api(tags="异步任务")
public class taskController {
    private final taskService taskService;
    //注入异步任务管理器
    private final AsyncTaskManager asyncTaskManager;

    @Autowired
    public taskController(taskService taskService, AsyncTaskManager asyncTaskManager) {
        this.taskService = taskService;
        this.asyncTaskManager = asyncTaskManager;
    }

    @ApiOperation(value="导出任务记录报表")
    @GetMapping("/get_task_excel")
    public Result get_task_excel(@RequestParam(required = false) Integer createuserid, HttpServletRequest request) {
        //调用任务管理器中的submit去提交一个异步任务
        String taskId = UUID.randomUUID().toString();
        Task task = new Task();
        task.setTaskId(taskId);
        task.setStatus(0);
        task.setMsg("开始执行任务!");
        task.setInfo("");
        task.setInfoType(0);
        task.setProgress(0);
        task.setCreateuserid(createuserid);
        task.setUpdatetime(DatePattern.getnow());
        if(taskService.insert_task(task)){
            Task task1 = asyncTaskManager.submit(() -> {
                System.out.println("_____start_____");
                System.out.println(taskId);
                Task asynctask = taskService.get_task(taskId);
                //获取数据列表,
                List<Task> lists = taskService.get_task_list2(createuserid);
                asynctask.setMsg("正在生成文件!");
                asynctask.setProgress(30);
                taskService.update_task(asynctask);
                File f = create_Excel(taskId, Blood.class, lists);
                if(f != null){
                    asynctask.setInfo(f.getName());
                    asynctask.setStatus(2);
                    asynctask.setMsg("文件已生成!");
                }else{
                    asynctask.setStatus(1);
                    asynctask.setMsg("文件生成失败!");
                }
                asynctask.setProgress(100);
                taskService.update_task(asynctask);
                System.out.println("_____end_____");
            }, taskId);
            if(task1 == null){
                return R.error("异步任务创建失败!");
            }
            return R.success(task1);
        };
        return R.error("异步任务创建失败!");
    }
    @ApiOperation(value="获取任务列表")
    @GetMapping("/get_task_list")
    public Result<Pager<Task>> get_task_list(@RequestParam Integer page,
                                                @RequestParam Integer size,
                                                HttpServletRequest request){
        Integer createuserid = 1;
        Pager<Task> res = taskService.get_task_list(page,size,createuserid);
        return R.success(res);
    }

   // @RequestMapping(value = "/start_task", method = RequestMethod.GET)
    public Result startAsyncTask() {
        //调用任务管理器中的submit去提交一个异步任务
        String taskId = UUID.randomUUID().toString();
        Task task = new Task();
        task.setTaskId(taskId);
        task.setStatus(0);
        task.setMsg("开始执行任务!");
        task.setInfo("");
        task.setInfoType(0);
        task.setProgress(0);
        task.setCreateuserid(1);
        task.setUpdatetime(DatePattern.getnow());
        if(taskService.insert_task(task)){
            Task task1 = asyncTaskManager.submit(() -> {
                System.out.println("_____start_____");
                try {
                    //模拟异步,睡眠6秒
                    Thread.sleep(30000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(taskId);
                System.out.println("_____end_____");
            }, taskId);
            if(task1 == null){
                return R.error("异步任务创建失败!");
            }
            return R.success(task1);
        };
        return R.error("异步任务创建失败!");
    }

    @RequestMapping(value = "/get_task", method = RequestMethod.GET)
    public Result getTaskStatus(
            @RequestParam("taskId") String taskId) {
        return R.success(asyncTaskManager.getTaskInfo(taskId));
    }

    @GetMapping("/{filename}")
    @ApiOperation(value="读取excel")
    public void getexcel(@PathVariable(name = "filename") String filename, HttpServletRequest request, HttpServletResponse response) throws Exception {
        response.setDateHeader("Expires", 0);
        response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
        response.addHeader("Cache-Control", "post-check=0, pre-check=0");
        response.setHeader("Pragma", "no-cache");
        BufferedInputStream bis = null;
        OutputStream out = null;
        try
        {
            InputStream inputStream = CodeUtil.getExcel(filename);
            out = response.getOutputStream();
            bis = new BufferedInputStream(inputStream);
            byte[] buff = new byte[1024 * 2];
            int count = -1;
            out = response.getOutputStream();   //直接下载导出
            while ((count = bis.read(buff)) != -1)
            {
                out.write(buff, 0, count);
            }
            out.flush();
            out.close();

        } catch (IOException e)
        {

        } finally
        {
            if (out != null)
            {
                try
                {
                    out.close();
                } catch (IOException e)
                {
                    e.printStackTrace();
                }
            }
        }

    }
}

tools里面的Result.java就是一个

package com.src.xxx.tools;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;

@ApiModel(value="通用返回结构体",description = "http请求返回的最外层对象")
public class Result<T> {
    @ApiModelProperty(value="错误码0成功1失败401不是超级管理员",name="code")
    private Integer code;

    @ApiModelProperty(value="提示信息",name="msg")
    private String msg;

    @ApiModelProperty(value="具体的内容",name="data")
    private T data;

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }
}

 R.java 就是定一个的一个常用方法之类的工具

package com.src.xxx.tools;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.DigestUtils;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

import static org.springframework.web.context.request.RequestContextHolder.getRequestAttributes;

@Slf4j
public class R {
    public static Result success(Object object) {
        Result result = new Result();
        result.setCode(0);
        result.setMsg("success");
        result.setData(object);
        return result;
    }

    public static Result notadmin() {
        Result result = new Result();
        result.setCode(401);
        result.setMsg("无权限");
        return result;
    }
   
    public static Result success() {
        return success(null);
    }

    public static Result error(String msg) {
        Result result = new Result();
        result.setCode(1);
        result.setMsg(msg);
        return result;
    }

}

主要代码如上,其中一些业务逻辑相关的例如登录验证这些都去掉了。临时把导出的数据表改成了task的表。

见笑了。

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐