8 changed files with 522 additions and 0 deletions
@ -0,0 +1,38 @@ |
|||||||
|
package com.biutag.supervision.mapper; |
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
||||||
|
import com.biutag.supervision.pojo.entity.price.ExcelRowData; |
||||||
|
import org.apache.ibatis.annotations.Param; |
||||||
|
|
||||||
|
/** |
||||||
|
* Excel导入数据Mapper |
||||||
|
* |
||||||
|
* @author system |
||||||
|
*/ |
||||||
|
public interface ExcelRowDataMapper extends BaseMapper<ExcelRowData> { |
||||||
|
|
||||||
|
/** |
||||||
|
* 分页模糊搜索Excel行数据 |
||||||
|
* 遍历row_data JSON中的所有value进行关键字匹配 |
||||||
|
* |
||||||
|
* @param page 分页对象 |
||||||
|
* @param keyword 搜索关键字 |
||||||
|
* @param batchId 批次ID(可选) |
||||||
|
* @return 分页结果 |
||||||
|
*/ |
||||||
|
Page<ExcelRowData> searchPage(@Param("page") Page<ExcelRowData> page, |
||||||
|
@Param("keyword") String keyword, |
||||||
|
@Param("batchId") String batchId); |
||||||
|
|
||||||
|
/** |
||||||
|
* 分页查询批次列表(按批次分组) |
||||||
|
* 返回每个批次的汇总信息:文件名、批次名称、上传时间、上传人等 |
||||||
|
* |
||||||
|
* @param page 分页对象 |
||||||
|
* @param keyword 搜索关键字(可选,搜索文件名或批次名称) |
||||||
|
* @return 分页结果 |
||||||
|
*/ |
||||||
|
Page<ExcelRowData> selectBatchListPage(@Param("page") Page<ExcelRowData> page, |
||||||
|
@Param("keyword") String keyword); |
||||||
|
} |
||||||
@ -0,0 +1,67 @@ |
|||||||
|
package com.biutag.supervision.pojo.entity.price; |
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType; |
||||||
|
import com.baomidou.mybatisplus.annotation.TableField; |
||||||
|
import com.baomidou.mybatisplus.annotation.TableId; |
||||||
|
import com.baomidou.mybatisplus.annotation.TableName; |
||||||
|
import io.swagger.v3.oas.annotations.media.Schema; |
||||||
|
import lombok.Data; |
||||||
|
|
||||||
|
import java.time.LocalDateTime; |
||||||
|
|
||||||
|
/** |
||||||
|
* Excel多Sheet导入数据实体 |
||||||
|
* 用于存储从Excel导入的每行数据,支持多Sheet导入和模糊搜索 |
||||||
|
* |
||||||
|
* @author system |
||||||
|
*/ |
||||||
|
@Data |
||||||
|
@TableName("excel_row_data") |
||||||
|
@Schema(description = "Excel导入行数据") |
||||||
|
public class ExcelRowData { |
||||||
|
|
||||||
|
@TableId(type = IdType.AUTO) |
||||||
|
@Schema(description = "主键ID") |
||||||
|
private Long id; |
||||||
|
|
||||||
|
@TableField("file_name") |
||||||
|
@Schema(description = "原文件名") |
||||||
|
private String fileName; |
||||||
|
|
||||||
|
@TableField("file_path") |
||||||
|
@Schema(description = "文件保存路径") |
||||||
|
private String filePath; |
||||||
|
|
||||||
|
@TableField("sheet_name") |
||||||
|
@Schema(description = "Sheet名称") |
||||||
|
private String sheetName; |
||||||
|
|
||||||
|
@TableField("row_index") |
||||||
|
@Schema(description = "Excel行号(从2开始)") |
||||||
|
private Integer rowIndex; |
||||||
|
|
||||||
|
@TableField("row_data") |
||||||
|
@Schema(description = "整行数据JSON,格式:{\"序号\":\"1\",\"名称\":\"aaa\",\"价格\":\"333\"}") |
||||||
|
private String rowData; |
||||||
|
|
||||||
|
@TableField("batch_id") |
||||||
|
@Schema(description = "批次号UUID,用于关联同一批次导入的数据") |
||||||
|
private String batchId; |
||||||
|
|
||||||
|
@TableField("batch_name") |
||||||
|
@Schema(description = "批次名称(用户自定义)") |
||||||
|
private String batchName; |
||||||
|
|
||||||
|
@TableField("upload_time") |
||||||
|
@Schema(description = "上传时间") |
||||||
|
private LocalDateTime uploadTime; |
||||||
|
|
||||||
|
@TableField("upload_user") |
||||||
|
@Schema(description = "上传人") |
||||||
|
private String uploadUser; |
||||||
|
|
||||||
|
|
||||||
|
@TableField("upload_user_name") |
||||||
|
@Schema(description = "上传人名字") |
||||||
|
private String uploadUserName; |
||||||
|
} |
||||||
@ -0,0 +1,27 @@ |
|||||||
|
package com.biutag.supervision.pojo.request.price; |
||||||
|
|
||||||
|
import com.biutag.supervision.aop.ParamChecked; |
||||||
|
import io.swagger.v3.oas.annotations.media.Schema; |
||||||
|
import lombok.Getter; |
||||||
|
import lombok.Setter; |
||||||
|
|
||||||
|
/** |
||||||
|
* Excel数据搜索请求参数 |
||||||
|
* |
||||||
|
* @author system |
||||||
|
*/ |
||||||
|
@Getter |
||||||
|
@Setter |
||||||
|
@Schema(description = "删除参数") |
||||||
|
public class ExcelDelRequest implements ParamChecked { |
||||||
|
|
||||||
|
@Schema(description = "批次") |
||||||
|
private String batchName; |
||||||
|
|
||||||
|
@Override |
||||||
|
public void check() { |
||||||
|
if (batchName == null) { |
||||||
|
throw new IllegalArgumentException("批次不能为空"); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,28 @@ |
|||||||
|
package com.biutag.supervision.pojo.request.price; |
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema; |
||||||
|
import lombok.Getter; |
||||||
|
import lombok.Setter; |
||||||
|
|
||||||
|
/** |
||||||
|
* Excel数据搜索请求参数 |
||||||
|
* |
||||||
|
* @author system |
||||||
|
*/ |
||||||
|
@Getter |
||||||
|
@Setter |
||||||
|
@Schema(description = "Excel数据搜索请求参数") |
||||||
|
public class ExcelSearchRequest { |
||||||
|
|
||||||
|
@Schema(description = "搜索关键字,会匹配row_data中的所有值") |
||||||
|
private String keyword; |
||||||
|
|
||||||
|
@Schema(description = "当前页码(从1开始)") |
||||||
|
private Integer current = 1; |
||||||
|
|
||||||
|
@Schema(description = "每页大小") |
||||||
|
private Integer size = 10; |
||||||
|
|
||||||
|
@Schema(description = "批次ID(可选,用于查询指定批次的数据)") |
||||||
|
private String batchId; |
||||||
|
} |
||||||
@ -0,0 +1,238 @@ |
|||||||
|
package com.biutag.supervision.service.Price; |
||||||
|
|
||||||
|
import cn.hutool.core.io.FileUtil; |
||||||
|
import com.alibaba.excel.EasyExcel; |
||||||
|
import com.alibaba.excel.ExcelReader; |
||||||
|
import com.alibaba.excel.context.AnalysisContext; |
||||||
|
import com.alibaba.excel.read.listener.ReadListener; |
||||||
|
import com.alibaba.excel.read.metadata.ReadSheet; |
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
||||||
|
import com.biutag.supervision.common.UserContextHolder; |
||||||
|
import com.biutag.supervision.mapper.ExcelRowDataMapper; |
||||||
|
import com.biutag.supervision.pojo.Result; |
||||||
|
import com.biutag.supervision.pojo.entity.price.ExcelRowData; |
||||||
|
import com.biutag.supervision.pojo.entity.price.PriceFile; |
||||||
|
import com.biutag.supervision.pojo.model.UserAuth; |
||||||
|
import com.biutag.supervision.pojo.request.price.ExcelDelRequest; |
||||||
|
import com.biutag.supervision.service.FileService; |
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper; |
||||||
|
import lombok.RequiredArgsConstructor; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
import org.springframework.stereotype.Service; |
||||||
|
import org.springframework.transaction.annotation.Transactional; |
||||||
|
import org.springframework.web.multipart.MultipartFile; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import java.io.InputStream; |
||||||
|
import java.time.LocalDateTime; |
||||||
|
import java.time.format.DateTimeFormatter; |
||||||
|
import java.util.*; |
||||||
|
|
||||||
|
@Slf4j |
||||||
|
@RequiredArgsConstructor |
||||||
|
@Service |
||||||
|
public class ExcelRowDataService extends ServiceImpl<ExcelRowDataMapper, ExcelRowData> { |
||||||
|
|
||||||
|
|
||||||
|
private final FileService fileService; |
||||||
|
|
||||||
|
@Transactional(rollbackFor = Exception.class) |
||||||
|
public Map<String, Object> importExcel(MultipartFile file) throws IOException { |
||||||
|
log.info("文件导入中------------------------------"); |
||||||
|
UserAuth user = UserContextHolder.getCurrentUser(); |
||||||
|
String filePath = fileService.upload(file); |
||||||
|
String fileNameType = FileUtil.extName(file.getOriginalFilename()); |
||||||
|
if (!"xls".equals(fileNameType) && !"xlsx".equals(fileNameType)) { |
||||||
|
throw new RuntimeException("仅支持 xls/xlsx 格式文件的导入"); |
||||||
|
} |
||||||
|
|
||||||
|
// 批次信息
|
||||||
|
String fileName = file.getOriginalFilename(); |
||||||
|
LocalDateTime uploadTime = LocalDateTime.now(); |
||||||
|
String batchId = uploadTime.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")); |
||||||
|
String batchName = fileName + batchId; |
||||||
|
|
||||||
|
log.info("开始解析表格文件: {}, 批次ID: {}", fileName, batchId); |
||||||
|
|
||||||
|
List<ExcelRowData> dataList = new ArrayList<>(); |
||||||
|
int totalRows = 0; |
||||||
|
|
||||||
|
// 获取Excel中所有Sheet信息
|
||||||
|
ExcelReader excelReader = EasyExcel.read(file.getInputStream()).build(); |
||||||
|
List<ReadSheet> sheets = excelReader.excelExecutor().sheetList(); |
||||||
|
excelReader.close(); |
||||||
|
|
||||||
|
// 用于存储每个Sheet的表头和数据的临时对象
|
||||||
|
// 修改:表头结构改为 表头名称 -> 列索引列表(支持合并单元格)
|
||||||
|
Map<String, List<Map<Integer, Object>>> sheetDataMap = new LinkedHashMap<>(); |
||||||
|
Map<String, Map<String, List<Integer>>> sheetHeaderMap = new LinkedHashMap<>(); |
||||||
|
|
||||||
|
// 遍历每个Sheet并读取数据
|
||||||
|
for (ReadSheet sheet : sheets) { |
||||||
|
try { |
||||||
|
InputStream inputStream = file.getInputStream(); |
||||||
|
final int[] sequenceRowIndex = {-1}; |
||||||
|
// 修改:表头Map结构:表头名称 -> 列索引列表
|
||||||
|
final Map<String, List<Integer>> headerMap = new LinkedHashMap<>(); |
||||||
|
final List<Map<Integer, Object>> allData = new ArrayList<>(); |
||||||
|
|
||||||
|
ExcelReader reader = EasyExcel.read(inputStream, new ReadListener<Map<Integer, Object>>() { |
||||||
|
@Override |
||||||
|
public void invoke(Map<Integer, Object> data, AnalysisContext context) { |
||||||
|
int currentRowIndex = context.readRowHolder().getRowIndex(); |
||||||
|
// 动态查找"序号"列所在行
|
||||||
|
if (sequenceRowIndex[0] == -1) { |
||||||
|
boolean hasSequenceColumn = data.values().stream() |
||||||
|
.anyMatch(v -> v != null && "序号".equals(v.toString().trim())); |
||||||
|
if (hasSequenceColumn) { |
||||||
|
sequenceRowIndex[0] = currentRowIndex; |
||||||
|
|
||||||
|
// ========== 修改:处理合并单元格逻辑 ==========
|
||||||
|
String lastHeaderName = null; |
||||||
|
for (Map.Entry<Integer, Object> entry : data.entrySet()) { |
||||||
|
Integer colIndex = entry.getKey(); |
||||||
|
Object cellValue = entry.getValue(); |
||||||
|
|
||||||
|
if (cellValue != null && !cellValue.toString().trim().isEmpty()) { |
||||||
|
// 有值的列,作为新的表头
|
||||||
|
lastHeaderName = cellValue.toString().trim(); |
||||||
|
headerMap.computeIfAbsent(lastHeaderName, k -> new ArrayList<>()).add(colIndex); |
||||||
|
} else if (lastHeaderName != null) { |
||||||
|
// 合并单元格(当前列为空),沿用上一个表头名称
|
||||||
|
headerMap.get(lastHeaderName).add(colIndex); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
allData.add(data); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void doAfterAllAnalysed(AnalysisContext context) { |
||||||
|
} |
||||||
|
}).build(); // ✅ 修正:括号位置正确
|
||||||
|
|
||||||
|
reader.read(EasyExcel.readSheet(sheet.getSheetNo()).build()); |
||||||
|
reader.close(); |
||||||
|
|
||||||
|
// 只有找到"序号"列的Sheet才处理
|
||||||
|
if (sequenceRowIndex[0] >= 0) { |
||||||
|
sheetHeaderMap.put(sheet.getSheetName(), headerMap); |
||||||
|
sheetDataMap.put(sheet.getSheetName(), allData); |
||||||
|
} |
||||||
|
} catch (Exception e) { |
||||||
|
log.warn("读取Sheet异常: {}, 错误: {}", sheet.getSheetName(), e.getMessage()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// ObjectMapper提到循环外部
|
||||||
|
ObjectMapper objectMapper = new ObjectMapper(); |
||||||
|
|
||||||
|
// 处理每个包含"序号"列的Sheet
|
||||||
|
for (Map.Entry<String, List<Map<Integer, Object>>> entry : sheetDataMap.entrySet()) { |
||||||
|
String sheetName = entry.getKey(); |
||||||
|
List<Map<Integer, Object>> allData = entry.getValue(); |
||||||
|
// 修改:类型匹配新的表头结构
|
||||||
|
Map<String, List<Integer>> headerMap = sheetHeaderMap.get(sheetName); |
||||||
|
|
||||||
|
// 查找"序号"所在行的索引
|
||||||
|
int sequenceRowIndex = -1; |
||||||
|
for (int i = 0; i < allData.size(); i++) { |
||||||
|
Map<Integer, Object> row = allData.get(i); |
||||||
|
if (row != null && row.values().stream().anyMatch(v -> v != null && "序号".equals(v.toString().trim()))) { |
||||||
|
sequenceRowIndex = i; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// 从"序号"行的下一行开始处理数据
|
||||||
|
for (int i = sequenceRowIndex + 1; i < allData.size(); i++) { |
||||||
|
Map<Integer, Object> row = allData.get(i); |
||||||
|
if (row == null || row.isEmpty()) continue; |
||||||
|
|
||||||
|
// ========== 修改:构建Map,处理合并单元格(多列对应同一表头) ==========
|
||||||
|
Map<String, Object> rowMap = new LinkedHashMap<>(); |
||||||
|
for (Map.Entry<String, List<Integer>> hEntry : headerMap.entrySet()) { |
||||||
|
String headerName = hEntry.getKey(); |
||||||
|
List<Integer> colIndices = hEntry.getValue(); |
||||||
|
|
||||||
|
List<Object> values = new ArrayList<>(); |
||||||
|
for (Integer colIndex : colIndices) { |
||||||
|
Object value = row.get(colIndex); |
||||||
|
values.add(value != null ? value : ""); |
||||||
|
} |
||||||
|
|
||||||
|
// 只有一个值直接存储,多个值用数组
|
||||||
|
if (values.size() == 1) { |
||||||
|
rowMap.put(headerName, values.get(0)); |
||||||
|
} else { |
||||||
|
rowMap.put(headerName, values); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// 序列化为 JSON 字符串
|
||||||
|
String rowDataJson = objectMapper.writeValueAsString(rowMap); |
||||||
|
|
||||||
|
ExcelRowData rowData = new ExcelRowData(); |
||||||
|
rowData.setFileName(fileName); |
||||||
|
rowData.setSheetName(sheetName); |
||||||
|
rowData.setRowIndex(i + 1); |
||||||
|
rowData.setRowData(rowDataJson); |
||||||
|
rowData.setBatchId(batchId); |
||||||
|
rowData.setBatchName(batchName); |
||||||
|
rowData.setUploadTime(uploadTime); |
||||||
|
rowData.setUploadUser(user.getUserName()); |
||||||
|
rowData.setUploadUserName(user.getNickName()); |
||||||
|
rowData.setFilePath(filePath); |
||||||
|
dataList.add(rowData); |
||||||
|
totalRows++; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// 批量保存
|
||||||
|
if (!dataList.isEmpty()) { |
||||||
|
saveBatch(dataList); |
||||||
|
} |
||||||
|
|
||||||
|
log.info("Excel导入完成,共导入 {} 行数据", totalRows); |
||||||
|
|
||||||
|
// 返回结果
|
||||||
|
Map<String, Object> result = new HashMap<>(); |
||||||
|
result.put("batchId", batchId); |
||||||
|
result.put("batchName", batchName); |
||||||
|
result.put("fileName", fileName); |
||||||
|
result.put("totalRows", totalRows); |
||||||
|
result.put("uploadTime", uploadTime); |
||||||
|
|
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
public Page<ExcelRowData> search(String keyword, String batchId, Integer current, Integer size) { |
||||||
|
Page<ExcelRowData> page = new Page<>(current, size); |
||||||
|
return baseMapper.searchPage(page, keyword, batchId); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 分页查询批次列表 |
||||||
|
* 返回每个批次的汇总信息 |
||||||
|
* |
||||||
|
* @param keyword 搜索关键字(可选) |
||||||
|
* @param current 当前页 |
||||||
|
* @param size 每页大小 |
||||||
|
* @return 分页结果 |
||||||
|
*/ |
||||||
|
public Page<ExcelRowData> getBatchListPage(String keyword, Integer current, Integer size) { |
||||||
|
Page<ExcelRowData> page = new Page<>(current, size); |
||||||
|
return baseMapper.selectBatchListPage(page, keyword); |
||||||
|
} |
||||||
|
|
||||||
|
public Result<Boolean> delExcelData(ExcelDelRequest excelDelRequest) { |
||||||
|
LambdaQueryWrapper<ExcelRowData> excelRowDataLambdaQueryWrapper = new LambdaQueryWrapper<>(); |
||||||
|
excelRowDataLambdaQueryWrapper.eq(ExcelRowData::getBatchName, excelDelRequest.getBatchName()); |
||||||
|
int delete = baseMapper.delete(excelRowDataLambdaQueryWrapper); |
||||||
|
return Result.success(Boolean.TRUE); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,57 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?> |
||||||
|
<!DOCTYPE mapper |
||||||
|
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" |
||||||
|
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
||||||
|
<mapper namespace="com.biutag.supervision.mapper.ExcelRowDataMapper"> |
||||||
|
|
||||||
|
<!-- 分页模糊搜索Excel行数据 --> |
||||||
|
<select id="searchPage" resultType="com.biutag.supervision.pojo.entity.price.ExcelRowData"> |
||||||
|
SELECT |
||||||
|
id, |
||||||
|
file_name AS fileName, |
||||||
|
file_path as filePath, |
||||||
|
sheet_name AS sheetName, |
||||||
|
row_index AS rowIndex, |
||||||
|
row_data AS rowData, |
||||||
|
batch_id AS batchId, |
||||||
|
batch_name AS batchName, |
||||||
|
upload_time AS uploadTime |
||||||
|
FROM excel_row_data |
||||||
|
WHERE 1=1 |
||||||
|
<if test="keyword != null and keyword != ''"> |
||||||
|
AND row_data LIKE CONCAT('%', #{keyword}, '%') |
||||||
|
</if> |
||||||
|
<if test="batchId != null and batchId != ''"> |
||||||
|
AND batch_id = #{batchId} |
||||||
|
</if> |
||||||
|
ORDER BY upload_time DESC, id ASC |
||||||
|
</select> |
||||||
|
|
||||||
|
<!-- 分页查询批次列表(按批次分组) --> |
||||||
|
<select id="selectBatchListPage" resultType="com.biutag.supervision.pojo.entity.price.ExcelRowData"> |
||||||
|
SELECT |
||||||
|
erd.id, |
||||||
|
erd.file_name AS fileName, |
||||||
|
erd.sheet_name AS sheetName, |
||||||
|
erd.batch_id AS batchId, |
||||||
|
erd.batch_name AS batchName, |
||||||
|
erd.upload_time AS uploadTime, |
||||||
|
erd.upload_user AS uploadUser, |
||||||
|
erd.upload_user_name AS uploadUserName, |
||||||
|
erd.file_path AS filePath, |
||||||
|
(SELECT COUNT(*) FROM excel_row_data WHERE batch_id = erd.batch_id) AS rowIndex |
||||||
|
FROM ( |
||||||
|
SELECT batch_id, MAX(id) AS max_id |
||||||
|
FROM excel_row_data |
||||||
|
GROUP BY batch_id |
||||||
|
) AS latest |
||||||
|
INNER JOIN excel_row_data erd ON erd.id = latest.max_id |
||||||
|
WHERE 1=1 |
||||||
|
<if test="keyword != null and keyword != ''"> |
||||||
|
AND (erd.file_name LIKE CONCAT('%', #{keyword}, '%') |
||||||
|
OR erd.batch_name LIKE CONCAT('%', #{keyword}, '%')) |
||||||
|
</if> |
||||||
|
ORDER BY erd.upload_time DESC |
||||||
|
</select> |
||||||
|
|
||||||
|
</mapper> |
||||||
@ -0,0 +1,14 @@ |
|||||||
|
-- Excel多Sheet导入数据表 |
||||||
|
CREATE TABLE `excel_row_data` ( |
||||||
|
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID', |
||||||
|
`file_name` VARCHAR(255) NOT NULL COMMENT '原文件名', |
||||||
|
`sheet_name` VARCHAR(255) NOT NULL COMMENT 'Sheet名称', |
||||||
|
`row_index` INT NOT NULL COMMENT 'Excel行号(从2开始)', |
||||||
|
`row_data` JSON NOT NULL COMMENT '整行数据JSON', |
||||||
|
`batch_id` VARCHAR(64) NOT NULL COMMENT '批次号UUID', |
||||||
|
`batch_name` VARCHAR(255) COMMENT '批次名称', |
||||||
|
`upload_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '上传时间', |
||||||
|
PRIMARY KEY (`id`), |
||||||
|
INDEX `idx_batch_id` (`batch_id`), |
||||||
|
INDEX `idx_upload_time` (`upload_time`) |
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; |
||||||
Loading…
Reference in new issue