From 42a92dca64b443c29dc8b4df9fac8b580e124f22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=80=A1?= Date: Thu, 24 Jul 2025 17:13:37 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=96=87=E4=BB=B6=E4=B8=8B?= =?UTF-8?q?=E8=BD=BD=E5=92=8C=E5=AF=86=E9=92=A5=E8=8E=B7=E5=8F=96=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=EF=BC=8C=E6=9B=B4=E6=96=B0=E7=94=A8=E6=88=B7=E6=9C=8D?= =?UTF-8?q?=E5=8A=A1=E4=BB=A5=E6=94=AF=E6=8C=81=E4=BB=8EExcel=E6=89=B9?= =?UTF-8?q?=E9=87=8F=E5=AF=BC=E5=85=A5=E7=94=A8=E6=88=B7=EF=BC=8C=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E7=94=A8=E6=88=B7=E5=AF=BC=E5=85=A5=E6=A8=A1=E6=9D=BF?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 34 +++--- .../files/controller/FileController.java | 23 ++++ .../files/service/SysStorageService.java | 4 +- .../service/impl/SysStorageServiceImpl.java | 107 +++++++++++++++++- .../service/impl/BaseUserServiceImpl.java | 38 +++---- .../user/controller/SysUserController.java | 28 +++++ .../user/entity/dto/SysUserExcelDTO.java | 57 ++++++++++ .../system/user/service/SysUserService.java | 7 ++ .../user/service/impl/SysUserServiceImpl.java | 43 +++++++ .../template/user_import_template.xlsx | Bin 0 -> 9433 bytes 10 files changed, 300 insertions(+), 41 deletions(-) create mode 100644 src/main/java/com/coscoshipping/ebtp/system/user/entity/dto/SysUserExcelDTO.java create mode 100644 src/main/resources/template/user_import_template.xlsx diff --git a/pom.xml b/pom.xml index 716c55f..192dc51 100644 --- a/pom.xml +++ b/pom.xml @@ -67,22 +67,28 @@ 3.17.4 + + com.alibaba + easyexcel + 3.1.0 + + - - - maven-snapshot-local - maven-snapshot-local - http://ccp.tianti.tg.unicom.local/artifactory/tianti-maven-snapshot-local/ - - false - - - true - always - - - + + + + + + + + + + + + + + diff --git a/src/main/java/com/coscoshipping/ebtp/system/files/controller/FileController.java b/src/main/java/com/coscoshipping/ebtp/system/files/controller/FileController.java index f5b0595..2a36002 100644 --- a/src/main/java/com/coscoshipping/ebtp/system/files/controller/FileController.java +++ b/src/main/java/com/coscoshipping/ebtp/system/files/controller/FileController.java @@ -382,5 +382,28 @@ public class FileController extends BaseController { return ok(aBoolean); } + /** + * 描述: 下载大附件 + * + * @author: MengHaoHao + * @Date 创建时间: 2021/6/24 + */ + @GetMapping("/getDownload") + public BaseResponse getDownload(@RequestParam("fileId") String fileId,HttpServletResponse httpServletResponse ,@RequestParam("documentSecretKey") String documentSecretKey){ + return fileService.getDownload(fileId,httpServletResponse,documentSecretKey); + } + + + /** + * 描述: 获取密钥 + * + * @author: MengHaoHao + * @Date 创建时间: 2021/6/24 + */ + @GetMapping("/getSecretKey") + public BaseResponse getSecretKey(){ + return fileService.getSecretKey(); + } + } diff --git a/src/main/java/com/coscoshipping/ebtp/system/files/service/SysStorageService.java b/src/main/java/com/coscoshipping/ebtp/system/files/service/SysStorageService.java index e0bd95f..78c2d8a 100644 --- a/src/main/java/com/coscoshipping/ebtp/system/files/service/SysStorageService.java +++ b/src/main/java/com/coscoshipping/ebtp/system/files/service/SysStorageService.java @@ -160,8 +160,8 @@ public interface SysStorageService extends IService { Boolean saveCutover(List sysStoragePOList); -// BaseResponse getDownload(String fileId, HttpServletResponse httpServletResponse, String documentSecretKey); + BaseResponse getDownload(String fileId, HttpServletResponse httpServletResponse, String documentSecretKey); -// BaseResponse getSecretKey(); + BaseResponse getSecretKey(); } diff --git a/src/main/java/com/coscoshipping/ebtp/system/files/service/impl/SysStorageServiceImpl.java b/src/main/java/com/coscoshipping/ebtp/system/files/service/impl/SysStorageServiceImpl.java index 5580f1d..ab1488b 100644 --- a/src/main/java/com/coscoshipping/ebtp/system/files/service/impl/SysStorageServiceImpl.java +++ b/src/main/java/com/coscoshipping/ebtp/system/files/service/impl/SysStorageServiceImpl.java @@ -1,8 +1,15 @@ package com.coscoshipping.ebtp.system.files.service.impl; +import com.amazonaws.ClientConfiguration; +import com.amazonaws.Protocol; +import com.amazonaws.auth.AWSCredentials; +import com.amazonaws.auth.BasicAWSCredentials; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.AmazonS3Client; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.chinaunicom.mall.ebtp.common.base.entity.BaseResponse; import com.chinaunicom.mall.ebtp.common.exception.entity.BusinessException; import com.coscoshipping.ebtp.system.common.framework.enums.ResponseEnum; import com.coscoshipping.ebtp.system.common.framework.response.BasePageResponse; @@ -20,25 +27,27 @@ import com.coscoshipping.ebtp.system.storage.common.StorageProperties; import com.coscoshipping.ebtp.system.storage.entity.Storage; import com.coscoshipping.ebtp.system.storage.service.StorageService; +import org.apache.commons.collections.CollectionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import org.springframework.util.Assert; import org.springframework.util.StringUtils; import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; +import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.*; import java.net.MalformedURLException; import java.net.URL; import java.net.URLEncoder; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; +import java.util.*; +import java.util.concurrent.TimeUnit; /** *

@@ -57,7 +66,8 @@ public class SysStorageServiceImpl extends ServiceImpl getDownload(String fileId, HttpServletResponse httpServletResponse, String documentSecretKey) { + + Object redisDocumentSecretKey = redisTemplate.opsForValue().get("documentSecretKey"); + if (redisDocumentSecretKey==null ||!documentSecretKey.equals(redisDocumentSecretKey.toString())){ + return new BaseResponse(0,false,"密钥不正确",false); + } + Set keys = redisTemplate.keys("documentSecretKey"); + if (CollectionUtils.isNotEmpty(keys)) { + this.redisTemplate.delete(keys); + } + + SysStoragePO sysStoragePO = this.baseMapper.selectFileById(fileId); + if (sysStoragePO == null) { + return new BaseResponse<>(0, false, "fileId不存在", null); + } + try { + httpServletResponse.setHeader("Content-Disposition", "attachment;filename=" + + URLEncoder.encode(sysStoragePO.getOriginalName(), "utf-8")); //originalName:这个下载附件名字 比如:abc.jpg + + } catch (Exception e) { + e.printStackTrace(); + } + + if ("0".equals(sysStoragePO.getCutoverStatus()) && sysStoragePO.getLogoFilePath() != null) { + try { + URL url = new URL(sysStoragePO.getLogoFilePath()); + InputStream inputStream = url.openStream(); + ServletOutputStream outputStream = httpServletResponse.getOutputStream(); + byte[] buffer = new byte[1024]; + int length; + while ((length = inputStream.read(buffer)) > 0) { + outputStream.write(buffer, 0, length); + } + } catch (Exception e) { + e.printStackTrace(); + return new BaseResponse<>(0, false, "下载割接附件失败", null); + } + } + try { + + // 因自定义HTTPS证书问题,忽略自定义证书报错,指定系统变量可忽略此错误; + System.setProperty("com.amazonaws.sdk.disableCertChecking", "true"); + + InputStream inputStream = initAmazonS3Object().getObject(properties.getBucketName(), sysStoragePO.getFilePath()).getObjectContent(); +// InputStream inputStream = uniStorageManager.downloadFileAsStream(sysStoragePO.getFilePath()); + ServletOutputStream outputStream = httpServletResponse.getOutputStream(); + byte[] buffer = new byte[1024]; + int length; + while ((length = inputStream.read(buffer)) > 0) { + outputStream.write(buffer, 0, length); + outputStream.flush(); + } + outputStream.close(); + } catch (Exception e) { + e.printStackTrace(); + return new BaseResponse<>(0,false,"下载3.0附件失败",null); + } + return new BaseResponse<>(1,true,"下载附件成功",null); + + } + + @Override + public BaseResponse getSecretKey() { + String ymd = System.currentTimeMillis() + UUID.randomUUID().toString(); + try { + this.redisTemplate.opsForValue().set("documentSecretKey", ymd); + this.redisTemplate.expire("documentSecretKey", 60l, TimeUnit.SECONDS); + return new BaseResponse(1, true, "获取密钥成功", ymd); + + } catch (Exception e) { + e.printStackTrace(); + } + + return new BaseResponse(0, false, "获取密钥失败,请稍后重试", null); + } + + private AmazonS3 initAmazonS3Object() { + // 新建一个凭证 + AWSCredentials credentials = new BasicAWSCredentials(properties.getAccessKey(), properties.getSecretKey()); + ClientConfiguration clientConfig = new ClientConfiguration(); + clientConfig.withProtocol(Protocol.HTTPS); + AmazonS3 amazonS3Client = new AmazonS3Client(credentials, clientConfig); + // 对象网关地址 + amazonS3Client.setEndpoint(properties.getEndpoint()); + return amazonS3Client; + } + } diff --git a/src/main/java/com/coscoshipping/ebtp/system/login/service/impl/BaseUserServiceImpl.java b/src/main/java/com/coscoshipping/ebtp/system/login/service/impl/BaseUserServiceImpl.java index affe6cd..94ea146 100644 --- a/src/main/java/com/coscoshipping/ebtp/system/login/service/impl/BaseUserServiceImpl.java +++ b/src/main/java/com/coscoshipping/ebtp/system/login/service/impl/BaseUserServiceImpl.java @@ -120,7 +120,7 @@ public class BaseUserServiceImpl extends BaseServiceImpl userList = this.list(new LambdaQueryWrapper().eq(SysUser::getEmployeeNumber, account) @@ -230,22 +230,18 @@ public class BaseUserServiceImpl extends BaseServiceImpl userList = sysSupplierUserMapper .selectList(new LambdaQueryWrapper().eq(SysSupplierUser::getUsername, account) .eq(SysSupplierUser::getStatus, Integer.parseInt(Constants.USER_STATUS_LIVE))); if (userList == null || userList.size() == 0) { - // 记录登录失败日志 - sysLoginLogService.recordLoginFail(account, SysLoginLog.USER_TYPE_SUPPLIER, loginIp, "无效用户", userAgent); throw new RuntimeException("无效用户!"); } SysSupplierUser user = userList.get(0); if (Constants.USER_STATUS_FREEZE.equals(user.getStatus().toString())) { - // 记录登录失败日志 - sysLoginLogService.recordLoginFail(account, SysLoginLog.USER_TYPE_SUPPLIER, loginIp, "账号已冻结", userAgent); throw new RuntimeException("账号已冻结!"); } @@ -254,8 +250,6 @@ public class BaseUserServiceImpl extends BaseServiceImpl userList = sysExpertUserMapper @@ -356,7 +352,7 @@ public class BaseUserServiceImpl extends BaseServiceImpl importUsers(@RequestParam("file") MultipartFile file) { + return BaseResponse.success(iSysUserService.importUsersFromExcel(file)); + } + + /** + * 下载用户导入模板 + */ + @ApiOperation("下载用户导入模板") + @GetMapping("/downloadTemplate") + public ResponseEntity downloadTemplate() throws IOException { + ClassPathResource resource = new ClassPathResource("template/user_import_template.xlsx"); + return ResponseEntity.ok() + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=user_import_template.xlsx") + .contentType(MediaType.parseMediaType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")) + .body(resource); + } } diff --git a/src/main/java/com/coscoshipping/ebtp/system/user/entity/dto/SysUserExcelDTO.java b/src/main/java/com/coscoshipping/ebtp/system/user/entity/dto/SysUserExcelDTO.java new file mode 100644 index 0000000..31b2471 --- /dev/null +++ b/src/main/java/com/coscoshipping/ebtp/system/user/entity/dto/SysUserExcelDTO.java @@ -0,0 +1,57 @@ +package com.coscoshipping.ebtp.system.user.entity.dto; + + +import com.alibaba.excel.annotation.ExcelProperty; + +public class SysUserExcelDTO { + @ExcelProperty("账号") + private String username; + @ExcelProperty("姓名") + private String realName; + @ExcelProperty("手机号") + private String phone; + @ExcelProperty("邮箱") + private String email; + @ExcelProperty("部门") + private String dept; + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getRealName() { + return realName; + } + + public void setRealName(String realName) { + this.realName = realName; + } + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getDept() { + return dept; + } + + public void setDept(String dept) { + this.dept = dept; + } +} diff --git a/src/main/java/com/coscoshipping/ebtp/system/user/service/SysUserService.java b/src/main/java/com/coscoshipping/ebtp/system/user/service/SysUserService.java index fd1e4d0..00265f1 100644 --- a/src/main/java/com/coscoshipping/ebtp/system/user/service/SysUserService.java +++ b/src/main/java/com/coscoshipping/ebtp/system/user/service/SysUserService.java @@ -62,4 +62,11 @@ public interface SysUserService extends IBaseService { * @return 是否成功 */ Boolean syncIamUser(com.coscoshipping.ebtp.system.user.entity.IamApiUser iamUser); + + /** + * 批量导入用户(Excel) + * @param file Excel文件 + * @return 是否成功 + */ + Boolean importUsersFromExcel(org.springframework.web.multipart.MultipartFile file); } diff --git a/src/main/java/com/coscoshipping/ebtp/system/user/service/impl/SysUserServiceImpl.java b/src/main/java/com/coscoshipping/ebtp/system/user/service/impl/SysUserServiceImpl.java index c122093..0dc18be 100644 --- a/src/main/java/com/coscoshipping/ebtp/system/user/service/impl/SysUserServiceImpl.java +++ b/src/main/java/com/coscoshipping/ebtp/system/user/service/impl/SysUserServiceImpl.java @@ -13,6 +13,7 @@ import com.coscoshipping.ebtp.system.org.service.SysOrgService; import com.coscoshipping.ebtp.system.role.entity.SysRole; import com.coscoshipping.ebtp.system.role.entity.vo.AllRolesAndAssignRoleVO; import com.coscoshipping.ebtp.system.user.entity.dto.SysInnerUserInfo; +import com.coscoshipping.ebtp.system.user.entity.dto.SysUserExcelDTO; import com.coscoshipping.ebtp.system.user.entity.vo.SysUserVO; import com.coscoshipping.ebtp.system.user.util.Md5Util; import com.coscoshipping.ebtp.system.userrole.service.SysUserRoleService; @@ -25,6 +26,12 @@ import com.chinaunicom.mall.ebtp.common.util.PropertyUtils; import com.coscoshipping.ebtp.system.user.dao.SysUserMapper; import com.coscoshipping.ebtp.system.user.entity.SysUser; import com.coscoshipping.ebtp.system.user.service.SysUserService; +import org.springframework.web.multipart.MultipartFile; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.support.ExcelTypeEnum; + +import java.util.ArrayList; import java.time.LocalDateTime; import java.util.List; @@ -159,6 +166,42 @@ public class SysUserServiceImpl extends BaseServiceImpl } } + @Override + public Boolean importUsersFromExcel(MultipartFile file) { + try { + String filename = file.getOriginalFilename(); + ExcelTypeEnum excelTypeEnum = com.alibaba.excel.support.ExcelTypeEnum.XLSX; + if (filename != null && filename.toLowerCase().endsWith(".xlsx")) { + excelTypeEnum = ExcelTypeEnum.XLSX; + } + List userList = com.alibaba.excel.EasyExcel.read(file.getInputStream(), SysUserExcelDTO.class, null) + .excelType(excelTypeEnum) + .sheet() + .doReadSync(); + List saveList = new ArrayList<>(); + for (SysUserExcelDTO dto : userList) { + if (dto.getUsername() == null || dto.getUsername().trim().isEmpty()) continue; + SysUser user = new SysUser(); + user.setUserId(PropertyUtils.getSnowflakeId()); + user.setName(dto.getRealName()); + user.setDisplayName(dto.getRealName()); + user.setMobile(dto.getPhone()); + user.setEmail(dto.getEmail()); + user.setOrgId(dto.getDept()); + user.setStatus(1); + user.setEmployeeNumber(dto.getUsername()); + user.setPassword(Md5Util.encode(resetPassword)); + saveList.add(user); + } + this.saveBatch(saveList); + return true; + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("导入失败: " + e.getMessage()); + } + } + + /** * 保存前的数据校验 */ diff --git a/src/main/resources/template/user_import_template.xlsx b/src/main/resources/template/user_import_template.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..2cb23606e101f4978a8929879fb83d95e5bb87c5 GIT binary patch literal 9433 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