Compare commits

...

1 Commits

Author SHA1 Message Date
wanjia 89cf31f69a feat(dms):excel导入校企成果,支持图片 2026-03-03 15:36:03 +08:00
10 changed files with 620 additions and 10 deletions

View File

@ -14,6 +14,7 @@ import org.springframework.web.multipart.MultipartFile;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 文件服务
@ -34,6 +35,17 @@ public interface RemoteFileService {
@RequestParam("hierarchy") String hierarchy,
@RequestHeader(SecurityConstants.FROM_SOURCE) String source);
/**
* Base64 图片上传HashMap 版本
*/
@PostMapping(value = "/upload/base64")
R<SysFileInfo> uploadBase64(
@RequestBody Map<String, Object> params,
@RequestParam("type") String type,
@RequestParam("hierarchy") String hierarchy,
@RequestHeader(SecurityConstants.FROM_SOURCE) String source
);
/**
* 根据文件Id列表获取文件列表
*

View File

@ -14,6 +14,7 @@ import org.springframework.web.multipart.MultipartFile;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 文件服务降级处理
@ -36,6 +37,11 @@ public class RemoteFileFallbackFactory implements FallbackFactory<RemoteFileServ
return R.fail("上传文件失败:" + throwable.getMessage());
}
@Override
public R<SysFileInfo> uploadBase64(Map<String, Object> params, String type, String hierarchy, String source) {
return R.fail("上传base64文件失败:" + throwable.getMessage());
}
@Override
public R<List<SysFileInfo>> getFileList(String fileIds) {
return R.fail("获取文件列表失败:" + throwable.getMessage());

View File

@ -0,0 +1,62 @@
package com.microservices.system.api.utils;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
public class StandardMultipartFile implements MultipartFile {
private final byte[] content;
private final String name;
private final String originalFilename;
private final String contentType;
public StandardMultipartFile(byte[] content, String name, String originalFilename, String contentType) {
this.content = content != null ? content : new byte[0];
this.name = name;
this.originalFilename = originalFilename;
this.contentType = contentType;
}
@Override
public String getName() {
return name;
}
@Override
public String getOriginalFilename() {
return originalFilename;
}
@Override
public String getContentType() {
return contentType;
}
@Override
public boolean isEmpty() {
return content.length == 0;
}
@Override
public long getSize() {
return content.length;
}
@Override
public byte[] getBytes() throws IOException {
return content;
}
@Override
public InputStream getInputStream() throws IOException {
return new ByteArrayInputStream(content);
}
@Override
public void transferTo(File dest) throws IOException, IllegalStateException {
try (FileOutputStream fos = new FileOutputStream(dest)) {
fos.write(content);
}
}
}

View File

@ -13,17 +13,19 @@ import com.microservices.common.core.utils.reflect.ReflectUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.RegExUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.util.IOUtils;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
import org.apache.poi.xssf.usermodel.XSSFDataValidation;
import org.apache.poi.xssf.usermodel.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
@ -1458,4 +1460,288 @@ public class ExcelUtil<T>
}
return method;
}
// 存储Excel中的图片key: 行号_列号value: MultipartFile
private final Map<String, MultipartFile> excelImageMap = new HashMap<>();
/**
* 获取解析后的Excel图片映射表行号_列号 -> MultipartFile
* <p>对外返回不可修改的Map避免外部篡改同时让静态检查识别到该集合被查询</p>
* @return 图片映射表
*/
public Map<String, MultipartFile> getExcelImageMap() {
// 返回不可修改的Map提升代码安全性同时让静态检查识别到集合被读取
return Collections.unmodifiableMap(excelImageMap);
}
/**
* 解析Excel中的图片并转换为MultipartFile存入{@link #excelImageMap}
* @param sheet Excel工作表
* @throws IOException 解析图片失败时抛出
*/
private void parseExcelImages(Sheet sheet) throws IOException {
if (wb instanceof XSSFWorkbook) {
parseXSSFImages((XSSFSheet) sheet);
} else if (wb instanceof HSSFWorkbook) {
parseHSSFImages((HSSFWorkbook) wb, sheet);
}
}
/**
* 解析XSSF格式xlsx图片
*/
private void parseXSSFImages(XSSFSheet sheet) {
XSSFDrawing drawing = sheet.createDrawingPatriarch();
List<XSSFShape> shapes = drawing.getShapes();
for (XSSFShape shape : shapes) {
if (shape instanceof XSSFPicture) {
XSSFPicture picture = (XSSFPicture) shape;
XSSFPictureData pictureData = picture.getPictureData();
XSSFClientAnchor anchor = picture.getPreferredSize();
// 获取图片所在单元格位置
int row = anchor.getRow1();
int col = anchor.getCol1();
String cellKey = row + "_" + col;
// 转换为MultipartFile并存储
MultipartFile multipartFile = bytesToMultipartFile(pictureData.getData(), "xlsx_image.png");
excelImageMap.put(cellKey, multipartFile);
}
}
}
/**
* 解析HSSF格式xls图片
*/
private void parseHSSFImages(HSSFWorkbook workbook, Sheet sheet) {
List<HSSFPictureData> pictureDataList = workbook.getAllPictures();
for (int i = 0; i < pictureDataList.size(); i++) {
HSSFPictureData pictureData = pictureDataList.get(i);
HSSFPatriarch patriarch = (HSSFPatriarch) sheet.createDrawingPatriarch();
List<HSSFShape> shapes = patriarch.getChildren();
for (HSSFShape shape : shapes) {
if (shape instanceof HSSFPicture) {
HSSFPicture picture = (HSSFPicture) shape;
if (picture.getPictureIndex() == i) {
HSSFClientAnchor anchor = picture.getClientAnchor();
int row = anchor.getRow1();
int col = anchor.getCol1();
String cellKey = row + "_" + col;
// 转换为MultipartFile并存储
MultipartFile multipartFile = bytesToMultipartFile(pictureData.getData(), "xls_image.png");
excelImageMap.put(cellKey, multipartFile);
break;
}
}
}
}
}
/**
* 自定义MultipartFile实现通用版无外部依赖
*/
public static class CustomMultipartFile implements MultipartFile {
private final byte[] content;
private final String filename;
private final String contentType;
public CustomMultipartFile(byte[] content, String filename, String contentType) {
this.content = content;
this.filename = filename;
this.contentType = contentType;
}
@Override
public String getName() {
return "file";
}
@Override
public String getOriginalFilename() {
return filename;
}
@Override
public String getContentType() {
return contentType;
}
@Override
public boolean isEmpty() {
return content == null || content.length == 0;
}
@Override
public long getSize() {
return content.length;
}
@Override
public byte[] getBytes() throws IOException {
return content;
}
@Override
public InputStream getInputStream() throws IOException {
return new ByteArrayInputStream(content);
}
@Override
public void transferTo(java.io.File dest) throws IOException, IllegalStateException {
try (java.io.OutputStream os = new java.io.FileOutputStream(dest)) {
os.write(content);
}
}
}
/**
* 将字节数组转换为MultipartFile核心工具方法
*/
public MultipartFile bytesToMultipartFile(byte[] bytes, String fileName) {
try {
String suffix = "png";
if (fileName != null && fileName.contains(".")) {
suffix = fileName.substring(fileName.lastIndexOf(".") + 1);
}
String finalFileName = UUID.randomUUID().toString() + "." + suffix;
String contentType = "image/" + suffix;
return new CustomMultipartFile(bytes, finalFileName, contentType);
} catch (Exception e) {
throw new RuntimeException("转换图片为MultipartFile失败", e);
}
}
// 导入包含图片的excel
public List<T> importExcelWithImages(String sheetName, InputStream is, int titleNum) throws Exception {
this.type = Type.IMPORT;
this.wb = WorkbookFactory.create(is);
List<T> list = new ArrayList<T>();
Sheet sheet = StringUtils.isNotEmpty(sheetName) ? wb.getSheet(sheetName) : wb.getSheetAt(0);
if (sheet == null) {
throw new IOException("文件sheet不存在");
}
// 1. 解析Excel中的图片生成imageMap行号_列号 -> MultipartFile
parseExcelImages(sheet);
int rows = sheet.getLastRowNum();
if (rows > 0) {
// ========== 参考原有逻辑构建表头列名和下标映射 ==========
Map<String, Integer> cellMap = new HashMap<>();
Row heard = sheet.getRow(titleNum);
for (int i = 0; i < heard.getPhysicalNumberOfCells(); i++) {
Cell cell = heard.getCell(i);
if (StringUtils.isNotNull(cell)) {
String columnName = this.getCellValue(heard, i).toString().trim();
cellMap.put(columnName, i);
} else {
cellMap.put(null, i);
}
}
// ========== 参考原有逻辑构建字段映射通过Excel注解匹配列名 ==========
List<Object[]> fields = this.getFields();
Map<Integer, Object[]> fieldsMap = new HashMap<Integer, Object[]>();
for (Object[] objects : fields) {
Excel attr = (Excel) objects[1];
Integer column = cellMap.get(attr.name());
if (column != null) {
fieldsMap.put(column, objects);
}
}
// 找到图片列的下标关键和Excel模板的列名对应
Integer imageColumnIndex = cellMap.get("图片");
// ========== 遍历数据行参考原有逻辑 ==========
for (int i = titleNum + 1; i <= rows; i++) {
Row row = sheet.getRow(i);
if (isRowEmpty(row)) {
continue;
}
T entity = null;
// 生成当前行的图片key行号_图片列下标i是数据行号对应Excel的行
String imageCellKey = i + "_" + imageColumnIndex;
// ========== 参考原有逻辑遍历字段映射赋值 ==========
for (Map.Entry<Integer, Object[]> entry : fieldsMap.entrySet()) {
Integer columnIndex = entry.getKey();
Object val = this.getCellValue(row, columnIndex);
// 如果不存在实例则新建
entity = (entity == null ? clazz.newInstance() : entity);
// 从map中得到对应列的field和Excel注解
Field field = (Field) entry.getValue()[0];
Excel attr = (Excel) entry.getValue()[1];
// ========== 关键修改如果是图片替换值为图片key ==========
if (imageColumnIndex != null && columnIndex.equals(imageColumnIndex)) {
val = imageCellKey; // 将单元格原始值替换为图片key如1_12
}
// ========== 原有类型转换逻辑完全复用 ==========
Class<?> fieldType = field.getType();
if (String.class == fieldType) {
String s = Convert.toStr(val);
if (StringUtils.endsWith(s, ".0")) {
val = StringUtils.substringBefore(s, ".0");
} else {
String dateFormat = field.getAnnotation(Excel.class).dateFormat();
if (StringUtils.isNotEmpty(dateFormat)) {
val = parseDateToStr(dateFormat, val);
} else {
val = Convert.toStr(val);
}
}
} else if ((Integer.TYPE == fieldType || Integer.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val))) {
val = Convert.toInt(val);
} else if ((Long.TYPE == fieldType || Long.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val))) {
val = Convert.toLong(val);
} else if (Double.TYPE == fieldType || Double.class == fieldType) {
val = Convert.toDouble(val);
} else if (Float.TYPE == fieldType || Float.class == fieldType) {
val = Convert.toFloat(val);
} else if (BigDecimal.class == fieldType) {
val = Convert.toBigDecimal(val);
} else if (Date.class == fieldType) {
if (val instanceof String) {
val = DateUtils.parseDate(val);
} else if (val instanceof Double) {
val = DateUtil.getJavaDate((Double) val);
}
} else if (Boolean.TYPE == fieldType || Boolean.class == fieldType) {
val = Convert.toBool(val, false);
}
// ========== 原有反射赋值逻辑完全复用 ==========
if (StringUtils.isNotNull(fieldType)) {
String propertyName = field.getName();
if (StringUtils.isNotEmpty(attr.targetAttr())) {
propertyName = field.getName() + "." + attr.targetAttr();
} else if (StringUtils.isNotEmpty(attr.readConverterExp())) {
val = reverseByExp(Convert.toStr(val), attr.readConverterExp(), attr.separator());
} else if (!attr.handler().equals(ExcelHandlerAdapter.class)) {
val = dataFormatHandlerAdapter(val, attr);
}
// 使用原有工具类反射赋值核心替换自定义setEntityFieldValue
ReflectUtils.invokeSetter(entity, propertyName, val);
}
}
if (entity != null) {
list.add(entity);
}
}
}
return list;
}
}

View File

@ -7,6 +7,7 @@ import com.microservices.common.core.web.domain.AjaxResult;
import com.microservices.common.core.web.page.TableDataInfo;
import com.microservices.common.log.annotation.Log;
import com.microservices.common.log.enums.BusinessType;
import com.microservices.common.security.utils.SecurityUtils;
import com.microservices.dms.achievementLibrary.domain.SchoolEnterpriseAchievements;
import com.microservices.dms.achievementLibrary.service.ISchoolEnterpriseAchievementsService;
import io.swagger.annotations.Api;
@ -15,9 +16,11 @@ import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Map;
/**
* 校企成果Controller
@ -90,6 +93,21 @@ public class SchoolEnterpriseAchievementsController extends BaseController
return AjaxResult.success(schoolEnterpriseAchievementsService.insertSchoolEnterpriseAchievements(schoolEnterpriseAchievements));
}
@PostMapping("/importData")
@Log(title = "校企成果", businessType = BusinessType.INSERT)
public AjaxResult importData(@RequestPart("file") MultipartFile file,
@RequestParam(value = "updateSupport", defaultValue = "false") boolean updateSupport) throws Exception {
ExcelUtil<SchoolEnterpriseAchievements> util = new ExcelUtil<>(SchoolEnterpriseAchievements.class);
// 1. 解析Excel获取实体列表和图片Map
List<SchoolEnterpriseAchievements> list = util.importExcelWithImages(StringUtils.EMPTY, file.getInputStream(), 0);
Map<String, MultipartFile> imageMap = util.getExcelImageMap();
// 2. 调用业务层处理传入实体列表和图片Map
String operName = SecurityUtils.getUsername();
String message = schoolEnterpriseAchievementsService.batchInsertSchoolEnterpriseAchievements(list, imageMap, updateSupport, operName);
return AjaxResult.success(message);
}
/**
* 修改校企成果
*/

View File

@ -167,7 +167,7 @@ public class Achievements extends BaseEntity {
/**
* 成果图片
*/
@Excel(name = "成果图片")
@Excel(name = "图片")
private String images;
/**

View File

@ -32,7 +32,10 @@ public class SchoolEnterpriseAchievements extends BaseEntity {
/**
* 成果领域1
*/
@Excel(name = "成果领域1")
@Excel(
name = "成果领域",
readConverterExp = "1=理论研究,2=政策法规,3=医学,4=电子信息,5=通信工程,6=计算机科学,7=软件工程,9=人工智能,10=知识图谱,12=大数据,23=机器人,24=无人系统,25=创意征集,26=文献翻译,27=成果转化,28=科技协同,29=科研组织模式,30=新兴领域,31=其他,32=电子元器件,33=测试仪器,34=军事历史,35=计算机网络"
)
private String field1;
/**
@ -50,7 +53,7 @@ public class SchoolEnterpriseAchievements extends BaseEntity {
/**
* 成果类型
*/
@Excel(name = "成果类型")
@Excel(name = "成果类型", readConverterExp = "1=理论成果,2=技术成果,3=应用成果,4=软科学成果,5=文化艺术成果,6=社会服务成果,7=交叉领域成果")
private String achievementType;
/**
@ -74,7 +77,10 @@ public class SchoolEnterpriseAchievements extends BaseEntity {
/**
* 成果标签
*/
@Excel(name = "成果标签")
@Excel(
name = "成果标签",
readConverterExp = "1=高校,2=企业,3=其他"
)
private String tags;
/**
@ -159,7 +165,7 @@ public class SchoolEnterpriseAchievements extends BaseEntity {
/**
* 成果图片
*/
@Excel(name = "成果图片")
@Excel(name = "图片")
private String images;
/**
@ -169,8 +175,15 @@ public class SchoolEnterpriseAchievements extends BaseEntity {
private String attachments;
private Integer achievementStatus;
//成果小类型
@Excel(
name = "成果小类型",
readConverterExp = "7=学术论文,8=学术专著,9=理论模型,10=发明专利,11=技术标准,12=软件系统,13=技术原型,14=产品设备,15=解决方案,16=示范工程,17=政策建议,18=咨询报告,19=行业标准,20=管理办法,21=文学作品,22=艺术作品,23=公益项目,24=社会调查报告,25=教育培训成果,26=理论模型+产品设备,27=学术论文+技术原型"
)
private String achievementLittleType;
//成果状态
@Excel(
name = "成果状态",
readConverterExp = "1=已有阶段性成果,2=已有成熟成果,3=已公开发表,4=已对外发布,5=已开展应用,6=已形成商业化产品"
)
private String achievementCurStatus;
//团队名称
private String teamName;

View File

@ -1,7 +1,10 @@
package com.microservices.dms.achievementLibrary.service;
import java.util.List;
import java.util.Map;
import com.microservices.dms.achievementLibrary.domain.SchoolEnterpriseAchievements;
import org.springframework.web.multipart.MultipartFile;
/**
* 校企成果Service接口
@ -64,4 +67,6 @@ public interface ISchoolEnterpriseAchievementsService
List<SchoolEnterpriseAchievements> listFront(SchoolEnterpriseAchievements schoolEnterpriseAchievements);
boolean getCheckSchoolAchName(SchoolEnterpriseAchievements schoolEnterpriseAchievements);
String batchInsertSchoolEnterpriseAchievements(List<SchoolEnterpriseAchievements> list, Map<String, MultipartFile> imageMap, boolean updateSupport, String operName);
}

View File

@ -1,25 +1,34 @@
package com.microservices.dms.achievementLibrary.service.impl;
import java.io.*;
import java.util.*;
import java.util.stream.Collectors;
import com.alibaba.fastjson2.JSONObject;
import com.microservices.common.core.constant.SecurityConstants;
import com.microservices.common.core.constant.UserConstants;
import com.microservices.common.core.domain.R;
import com.microservices.common.core.exception.ServiceException;
import com.microservices.common.core.utils.DateUtils;
import com.microservices.common.core.utils.StringUtils;
import com.microservices.common.datasource.annotation.Slave;
import com.microservices.common.security.utils.SecurityUtils;
import com.microservices.dms.achievementLibrary.domain.AchievementTeam;
import com.microservices.dms.achievementLibrary.domain.Achievements;
import com.microservices.dms.achievementLibrary.mapper.AchievementTeamMapper;
import com.microservices.dms.achievementLibrary.mapper.AchievementsMapper;
import com.microservices.dms.achievementLibrary.service.IAchievementsService;
import com.microservices.system.api.domain.SysUser;
import com.microservices.system.api.RemoteFileService;
import com.microservices.system.api.model.LoginUser;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.microservices.dms.achievementLibrary.mapper.SchoolEnterpriseAchievementsMapper;
import com.microservices.dms.achievementLibrary.domain.SchoolEnterpriseAchievements;
import com.microservices.dms.achievementLibrary.service.ISchoolEnterpriseAchievementsService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.commons.CommonsMultipartFile;
/**
* 校企成果Service业务层处理
@ -41,6 +50,9 @@ public class SchoolEnterpriseAchievementsServiceImpl implements ISchoolEnterpris
@Autowired
private AchievementsMapper achievementsMapper;
@Autowired
private RemoteFileService remoteFileService;
/**
* 查询校企成果
*
@ -124,6 +136,144 @@ public class SchoolEnterpriseAchievementsServiceImpl implements ISchoolEnterpris
return UserConstants.UNIQUE;
}
/**
* 导入成果数据包含图片处理
*/
@Override
@Transactional
@Slave
public String batchInsertSchoolEnterpriseAchievements(List<SchoolEnterpriseAchievements> list,
Map<String, MultipartFile> imageMap,
boolean updateSupport,
String operName) {
if (list == null || list.isEmpty()) {
throw new RuntimeException("导入成果数据不能为空!");
}
int successNum = 0;
int failureNum = 0;
int imageErrorNum = 0; // 新增图片处理失败数
StringBuilder successMsg = new StringBuilder();
StringBuilder failureMsg = new StringBuilder();
StringBuilder imageErrorMsg = new StringBuilder(); // 新增图片错误信息
for (SchoolEnterpriseAchievements schoolEnterpriseAchievement : list) {
try {
// 处理图片字段优化后返回处理结果不抛出异常
String imageHandleResult = handleAchievementImage(schoolEnterpriseAchievement, imageMap);
if (imageHandleResult.contains("失败") || imageHandleResult.contains("异常")) {
imageErrorNum++;
imageErrorMsg.append("<br/>").append(imageErrorNum).append("、成果 ").append(schoolEnterpriseAchievement.getAchievementName()).append(" 图片处理失败:").append(imageHandleResult);
}
// 原有查重和入库逻辑不受图片处理影响
SchoolEnterpriseAchievements exist = schoolEnterpriseAchievementsMapper.selectSchoolEnterpriseAchievementsByName(schoolEnterpriseAchievement.getAchievementName());
if (exist == null) {
schoolEnterpriseAchievement.setCreateBy(operName);
schoolEnterpriseAchievement.setCreateTime(new Date());
schoolEnterpriseAchievement.setUpdateBy(operName);
schoolEnterpriseAchievement.setUpdateTime(new Date());
schoolEnterpriseAchievement.setStatus("0");
schoolEnterpriseAchievement.setAchievementStatus(0);
schoolEnterpriseAchievementsMapper.insertSchoolEnterpriseAchievements(schoolEnterpriseAchievement);
successNum++;
successMsg.append("<br/>").append(successNum).append("、成果 ").append(schoolEnterpriseAchievement.getAchievementName()).append(" 导入成功");
} else {
if (updateSupport) {
schoolEnterpriseAchievement.setId(exist.getId());
schoolEnterpriseAchievement.setCreateBy(exist.getCreateBy());
schoolEnterpriseAchievement.setCreateTime(exist.getCreateTime());
schoolEnterpriseAchievement.setUpdateBy(operName);
schoolEnterpriseAchievement.setUpdateTime(new Date());
schoolEnterpriseAchievementsMapper.updateSchoolEnterpriseAchievements(schoolEnterpriseAchievement);
successNum++;
successMsg.append("<br/>").append(successNum).append("、成果 ").append(schoolEnterpriseAchievement.getAchievementName()).append(" 更新成功");
} else {
failureNum++;
failureMsg.append("<br/>").append(failureNum).append("、成果 ").append(schoolEnterpriseAchievement.getAchievementName()).append(" 已存在(未开启更新)");
}
}
} catch (Exception e) {
failureNum++;
String msg = "<br/>" + failureNum + "、成果 " + schoolEnterpriseAchievement.getAchievementName() + " 导入失败:";
failureMsg.append(msg).append(e.getMessage());
}
}
// 组装最终提示信息包含图片处理错误
StringBuilder finalMsg = new StringBuilder();
if (successNum > 0) {
finalMsg.append("").append(successNum).append(" 条数据导入/更新成功:").append(successMsg);
}
if (failureNum > 0) {
finalMsg.append("<br/>共 ").append(failureNum).append(" 条数据导入失败:").append(failureMsg);
}
if (imageErrorNum > 0) {
finalMsg.append("<br/>共 ").append(imageErrorNum).append(" 条数据图片处理失败:").append(imageErrorMsg);
}
return finalMsg.toString();
}
/**
* 处理成果图片上传并格式化存储FastJSON解析版
* @param achievement 成果实体
* @param imageMap Excel解析出的图片映射表
* @return 图片处理结果提示成功/失败信息
*/
private String handleAchievementImage(SchoolEnterpriseAchievements achievement, Map<String, MultipartFile> imageMap) {
String imageCellKey = achievement.getImages();
if (StringUtils.isEmpty(imageCellKey) || !imageMap.containsKey(imageCellKey)) {
return "无图片,跳过处理";
}
try {
MultipartFile file = imageMap.get(imageCellKey);
byte[] fileBytes = file.getBytes();
if (fileBytes == null || fileBytes.length == 0) {
return "图片文件为空,跳过处理";
}
// 构建 HashMap 参数
String originalFilename = StringUtils.isEmpty(file.getOriginalFilename())
? "excel_import_image_" + System.currentTimeMillis() + ".png"
: file.getOriginalFilename();
Map<String, Object> params = new HashMap<>();
params.put("base64Data", Base64.getEncoder().encodeToString(fileBytes));
params.put("fileName", originalFilename);
params.put("contentType", file.getContentType());
params.put("size", fileBytes.length);
// 调用 Base64 上传接口
R<?> fileResult = remoteFileService.uploadBase64(params, "dms", "import", SecurityConstants.INNER);
// 解析结果与原逻辑相同
if (StringUtils.isNull(fileResult) || StringUtils.isNull(fileResult.getData())) {
String errorMsg = !StringUtils.isNull(fileResult) ? fileResult.getMsg() : "接口返回异常";
return "图片上传失败:" + errorMsg;
}
JSONObject resultJson = JSONObject.parseObject(JSONObject.toJSONString(fileResult.getData()));
String fileOriginName = resultJson.getString("fileOriginName");
String url = resultJson.getString("downloadUrl");
Integer fileId = resultJson.getInteger("fileId");
if (StringUtils.isEmpty(fileOriginName) || StringUtils.isEmpty(url) || fileId == null) {
return "图片上传成功,但返回数据不完整";
}
String imageJson = String.format("[{\"k\":\"%s\",\"v\":\"%s\",\"id\":%d}]",
fileOriginName, url, fileId);
achievement.setImages(imageJson);
return "图片处理成功";
} catch (Exception e) {
return "图片处理异常:" + e.getMessage();
}
}
/**
* 修改校企成果

View File

@ -1,20 +1,26 @@
package com.microservices.file.controller;
import com.microservices.common.core.domain.R;
import com.microservices.common.core.utils.StringUtils;
import com.microservices.common.core.web.controller.BaseController;
import com.microservices.common.security.annotation.InnerAuth;
import com.microservices.file.service.ISysFileInfoService;
import com.microservices.system.api.domain.SysFile;
import com.microservices.system.api.domain.SysFileInfo;
import com.microservices.system.api.utils.StandardMultipartFile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import springfox.documentation.annotations.ApiIgnore;
import java.util.Base64;
import java.util.Map;
/**
* 文件请求处理
*
@ -49,4 +55,56 @@ public class SysFileController extends BaseController {
return R.fail(e.getMessage());
}
}
@InnerAuth
@PostMapping("/upload/base64")
public R<SysFileInfo> uploadBase64(@RequestBody Map<String, Object> params,
@RequestParam("type") String type,
@RequestParam("hierarchy") String hierarchy) {
try {
// 1. 提取参数
String base64Data = (String) params.get("base64Data");
String fileName = (String) params.get("fileName");
String contentType = (String) params.get("contentType");
// 2. 参数校验
if (StringUtils.isEmpty(base64Data)) {
return R.fail("base64Data不能为空");
}
// 3. Base64解码
byte[] fileBytes;
try {
fileBytes = Base64.getDecoder().decode(base64Data);
} catch (IllegalArgumentException e) {
log.error("Base64解码失败", e);
return R.fail("图片数据格式错误");
}
if (fileBytes.length == 0) {
return R.fail("解码后文件为空");
}
// 4. 构造MultipartFile
String originalFilename = StringUtils.isEmpty(fileName)
? "base64_image_" + System.currentTimeMillis() + ".png"
: fileName;
MultipartFile multipartFile = new StandardMultipartFile(
fileBytes,
"file", // 字段名与原接口一致
originalFilename,
contentType
);
// 5. 复用原有上传逻辑
SysFileInfo fileInfo = sysFileInfoService.upload(multipartFile, type, hierarchy);
return R.ok(fileInfo);
} catch (Exception e) {
log.error("Base64上传文件失败", e);
return R.fail(e.getMessage());
}
}
}