feat(ai-report): 新增委托单位管理和报告审批功能
- 新增委托单位管理模块,支持增删改查、导入导出功能 - 实现委托单位 Excel 模板下载和数据导入功能 - 添加报告审批抽象处理器,支持审批流程日志记录 - 统一文件存储路径配置,将各模块独立路径改为统一根目录 - 新增 PQDIF 文件名存储字段,完善文件管理功能 - 修复代码中的乱码问题和错误提示信息 - 优化系统常量定义和字典配置管理
This commit is contained in:
@@ -11,6 +11,7 @@
|
||||
</parent>
|
||||
|
||||
<artifactId>client-unit</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
@@ -30,5 +31,27 @@
|
||||
<artifactId>spingboot2.3.12</artifactId>
|
||||
<version>2.3.12</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.njcn.gather</groupId>
|
||||
<artifactId>system</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.poi</groupId>
|
||||
<artifactId>poi-ooxml</artifactId>
|
||||
<version>4.1.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.njcn.gather.aireport.clientunit.component;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.njcn.gather.aireport.clientunit.mapper.ClientUnitMapper;
|
||||
import com.njcn.gather.aireport.clientunit.pojo.vo.ClientUnitUserNameVO;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class ClientUnitUserNameResolver {
|
||||
|
||||
private final ClientUnitMapper clientUnitMapper;
|
||||
|
||||
public Map<String, String> resolveUserNames(Collection<String> userIds) {
|
||||
if (userIds == null || userIds.isEmpty()) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
Set<String> validIds = userIds.stream()
|
||||
.filter(StrUtil::isNotBlank)
|
||||
.collect(Collectors.toCollection(LinkedHashSet::new));
|
||||
if (validIds.isEmpty()) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
List<ClientUnitUserNameVO> users = clientUnitMapper.selectUserNamesByIds(validIds);
|
||||
if (users == null || users.isEmpty()) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
return users.stream()
|
||||
.filter(user -> user != null && StrUtil.isNotBlank(user.getId()) && StrUtil.isNotBlank(user.getName()))
|
||||
.collect(Collectors.toMap(ClientUnitUserNameVO::getId, ClientUnitUserNameVO::getName, (first, second) -> first));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package com.njcn.gather.aireport.clientunit.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.njcn.common.pojo.annotation.OperateInfo;
|
||||
import com.njcn.common.pojo.constant.OperateType;
|
||||
import com.njcn.common.pojo.enums.common.LogEnum;
|
||||
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
import com.njcn.common.pojo.response.HttpResult;
|
||||
import com.njcn.gather.aireport.clientunit.pojo.param.ClientUnitParam;
|
||||
import com.njcn.gather.aireport.clientunit.pojo.vo.ClientUnitImportResultVO;
|
||||
import com.njcn.gather.aireport.clientunit.pojo.vo.ClientUnitVO;
|
||||
import com.njcn.gather.aireport.clientunit.service.IClientUnitService;
|
||||
import com.njcn.web.controller.BaseController;
|
||||
import com.njcn.web.utils.HttpResultUtil;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 委托单位管理接口。
|
||||
*/
|
||||
@Api(tags = "委托单位管理")
|
||||
@RestController
|
||||
@RequestMapping("/clientUnit")
|
||||
@RequiredArgsConstructor
|
||||
public class ClientUnitController extends BaseController {
|
||||
|
||||
private final IClientUnitService clientUnitService;
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@ApiOperation("查询委托单位列表")
|
||||
@PostMapping("/list")
|
||||
public HttpResult<Page<ClientUnitVO>> list(@RequestBody(required = false) ClientUnitParam.QueryParam param) {
|
||||
String methodDescribe = getMethodDescribe("list");
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS,
|
||||
clientUnitService.listClientUnits(param), methodDescribe);
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.ADD)
|
||||
@ApiOperation("新增委托单位")
|
||||
@PostMapping("/add")
|
||||
public HttpResult<Boolean> add(@RequestBody @Validated ClientUnitParam.AddParam param) {
|
||||
String methodDescribe = getMethodDescribe("add");
|
||||
boolean result = clientUnitService.addClientUnit(param);
|
||||
return HttpResultUtil.assembleCommonResponseResult(result ? CommonResponseEnum.SUCCESS : CommonResponseEnum.FAIL,
|
||||
result, methodDescribe);
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.UPDATE)
|
||||
@ApiOperation("编辑委托单位")
|
||||
@PostMapping("/update")
|
||||
public HttpResult<Boolean> update(@RequestBody @Validated ClientUnitParam.UpdateParam param) {
|
||||
String methodDescribe = getMethodDescribe("update");
|
||||
boolean result = clientUnitService.updateClientUnit(param);
|
||||
return HttpResultUtil.assembleCommonResponseResult(result ? CommonResponseEnum.SUCCESS : CommonResponseEnum.FAIL,
|
||||
result, methodDescribe);
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@ApiOperation("查询委托单位详情")
|
||||
@ApiImplicitParam(name = "id", value = "委托单位ID", required = true)
|
||||
@GetMapping("/getById")
|
||||
public HttpResult<ClientUnitVO> getById(@RequestParam("id") String id) {
|
||||
String methodDescribe = getMethodDescribe("getById");
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS,
|
||||
clientUnitService.getClientUnit(id), methodDescribe);
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.DELETE)
|
||||
@ApiOperation("删除委托单位")
|
||||
@PostMapping("/delete")
|
||||
public HttpResult<Boolean> delete(@RequestBody List<String> ids) {
|
||||
String methodDescribe = getMethodDescribe("delete");
|
||||
boolean result = clientUnitService.deleteClientUnits(ids);
|
||||
return HttpResultUtil.assembleCommonResponseResult(result ? CommonResponseEnum.SUCCESS : CommonResponseEnum.FAIL,
|
||||
result, methodDescribe);
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@ApiOperation("导出委托单位")
|
||||
@PostMapping("/export")
|
||||
public void export(@RequestBody(required = false) ClientUnitParam.QueryParam param) {
|
||||
clientUnitService.exportClientUnits(param);
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@ApiOperation("下载委托单位导入模板")
|
||||
@GetMapping("/importTemplate")
|
||||
public void importTemplate() {
|
||||
clientUnitService.downloadImportTemplate();
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.ADD)
|
||||
@ApiOperation("导入委托单位")
|
||||
@PostMapping(value = "/import", consumes = {"multipart/form-data"})
|
||||
public HttpResult<ClientUnitImportResultVO> importExcel(@RequestParam("file") MultipartFile file) {
|
||||
String methodDescribe = getMethodDescribe("importExcel");
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS,
|
||||
clientUnitService.importClientUnits(file), methodDescribe);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.njcn.gather.aireport.clientunit.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.njcn.gather.aireport.clientunit.pojo.po.ClientUnitPO;
|
||||
import com.njcn.gather.aireport.clientunit.pojo.vo.ClientUnitUserNameVO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface ClientUnitMapper extends BaseMapper<ClientUnitPO> {
|
||||
|
||||
@Select({
|
||||
"<script>",
|
||||
"select id, name from sys_user where id in",
|
||||
"<foreach collection='ids' item='id' open='(' separator=',' close=')'>",
|
||||
"#{id}",
|
||||
"</foreach>",
|
||||
"</script>"
|
||||
})
|
||||
List<ClientUnitUserNameVO> selectUserNamesByIds(@Param("ids") Collection<String> ids);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.njcn.gather.aireport.clientunit.pojo.constant;
|
||||
|
||||
/**
|
||||
* 委托单位业务常量。
|
||||
*/
|
||||
public final class ClientUnitConst {
|
||||
|
||||
public static final int STATUS_DELETED = 0;
|
||||
public static final int STATUS_NORMAL = 1;
|
||||
|
||||
private ClientUnitConst() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.njcn.gather.aireport.clientunit.pojo.param;
|
||||
|
||||
import com.njcn.web.pojo.param.BaseParam;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
/**
|
||||
* 委托单位请求参数。
|
||||
*/
|
||||
public class ClientUnitParam {
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public static class QueryParam extends BaseParam {
|
||||
@ApiModelProperty("单位名称关键字")
|
||||
private String name;
|
||||
|
||||
@ApiModelProperty("创建开始时间,yyyy-MM-dd HH:mm:ss")
|
||||
private String searchBeginTime;
|
||||
|
||||
@ApiModelProperty("创建结束时间,yyyy-MM-dd HH:mm:ss")
|
||||
private String searchEndTime;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class AddParam {
|
||||
@ApiModelProperty("单位名称")
|
||||
@NotBlank(message = "单位名称不能为空")
|
||||
@Size(max = 32, message = "单位名称不能超过32个字符")
|
||||
private String name;
|
||||
|
||||
@ApiModelProperty("联系人")
|
||||
@Size(max = 200, message = "联系人不能超过200个字符")
|
||||
private String contactName;
|
||||
|
||||
@ApiModelProperty("联系方式")
|
||||
@Size(max = 32, message = "联系方式不能超过32个字符")
|
||||
private String phonenumber;
|
||||
|
||||
@ApiModelProperty("地址")
|
||||
@Size(max = 128, message = "地址不能超过128个字符")
|
||||
private String address;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class UpdateParam extends AddParam {
|
||||
@ApiModelProperty("委托单位ID")
|
||||
@NotBlank(message = "委托单位ID不能为空")
|
||||
private String id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.njcn.gather.aireport.clientunit.pojo.po;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 委托单位记录。
|
||||
*/
|
||||
@Data
|
||||
@TableName("ai_clientunit")
|
||||
public class ClientUnitPO implements Serializable {
|
||||
private static final long serialVersionUID = 2517699918054153742L;
|
||||
|
||||
@TableId("id")
|
||||
private String id;
|
||||
@TableField("name")
|
||||
private String name;
|
||||
@TableField("contact_name")
|
||||
private String contactName;
|
||||
@TableField("phonenumber")
|
||||
private String phonenumber;
|
||||
@TableField("address")
|
||||
private String address;
|
||||
@TableField("status")
|
||||
private Integer status;
|
||||
@TableField("create_by")
|
||||
private String createBy;
|
||||
@TableField("create_time")
|
||||
private LocalDateTime createTime;
|
||||
@TableField("update_by")
|
||||
private String updateBy;
|
||||
@TableField("update_time")
|
||||
private LocalDateTime updateTime;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.njcn.gather.aireport.clientunit.pojo.vo;
|
||||
|
||||
import cn.afterturn.easypoi.excel.annotation.Excel;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 委托单位 Excel 行对象。
|
||||
*/
|
||||
@Data
|
||||
public class ClientUnitExcelVO implements Serializable {
|
||||
private static final long serialVersionUID = 8533457988358235671L;
|
||||
|
||||
@Excel(name = "单位名称", width = 25)
|
||||
private String name;
|
||||
|
||||
@Excel(name = "联系人", width = 20)
|
||||
private String contactName;
|
||||
|
||||
@Excel(name = "联系方式", width = 20)
|
||||
private String phonenumber;
|
||||
|
||||
@Excel(name = "地址", width = 35)
|
||||
private String address;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.njcn.gather.aireport.clientunit.pojo.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 委托单位导入结果。
|
||||
*/
|
||||
@Data
|
||||
public class ClientUnitImportResultVO implements Serializable {
|
||||
private static final long serialVersionUID = 509902603493571405L;
|
||||
|
||||
private int successCount;
|
||||
private int failCount;
|
||||
private List<String> failReasons = new ArrayList<String>();
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.njcn.gather.aireport.clientunit.pojo.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ClientUnitUserNameVO {
|
||||
private String id;
|
||||
private String name;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.njcn.gather.aireport.clientunit.pojo.vo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
|
||||
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
|
||||
import lombok.Data;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 委托单位展示对象。
|
||||
*/
|
||||
@Data
|
||||
public class ClientUnitVO implements Serializable {
|
||||
private static final long serialVersionUID = 8924649462626990262L;
|
||||
private static final String DEFAULT_EMPTY_DISPLAY = "-";
|
||||
|
||||
private String id;
|
||||
private String name;
|
||||
private String contactName;
|
||||
private String phonenumber;
|
||||
private String address;
|
||||
private String createBy;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
|
||||
@JsonSerialize(using = LocalDateTimeSerializer.class)
|
||||
private LocalDateTime createTime;
|
||||
private String updateBy;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
|
||||
@JsonSerialize(using = LocalDateTimeSerializer.class)
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
public String getId() {
|
||||
return defaultDisplay(id);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return defaultDisplay(name);
|
||||
}
|
||||
|
||||
public String getContactName() {
|
||||
return defaultDisplay(contactName);
|
||||
}
|
||||
|
||||
public String getPhonenumber() {
|
||||
return defaultDisplay(phonenumber);
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
return defaultDisplay(address);
|
||||
}
|
||||
|
||||
public String getCreateBy() {
|
||||
return defaultDisplay(createBy);
|
||||
}
|
||||
|
||||
public String getUpdateBy() {
|
||||
return defaultDisplay(updateBy);
|
||||
}
|
||||
|
||||
private String defaultDisplay(String value) {
|
||||
return StringUtils.hasText(value) ? value : DEFAULT_EMPTY_DISPLAY;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.njcn.gather.aireport.clientunit.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.njcn.gather.aireport.clientunit.pojo.param.ClientUnitParam;
|
||||
import com.njcn.gather.aireport.clientunit.pojo.po.ClientUnitPO;
|
||||
import com.njcn.gather.aireport.clientunit.pojo.vo.ClientUnitImportResultVO;
|
||||
import com.njcn.gather.aireport.clientunit.pojo.vo.ClientUnitVO;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface IClientUnitService extends IService<ClientUnitPO> {
|
||||
|
||||
Page<ClientUnitVO> listClientUnits(ClientUnitParam.QueryParam param);
|
||||
|
||||
boolean addClientUnit(ClientUnitParam.AddParam param);
|
||||
|
||||
boolean updateClientUnit(ClientUnitParam.UpdateParam param);
|
||||
|
||||
ClientUnitVO getClientUnit(String id);
|
||||
|
||||
boolean deleteClientUnits(List<String> ids);
|
||||
|
||||
void exportClientUnits(ClientUnitParam.QueryParam param);
|
||||
|
||||
void downloadImportTemplate();
|
||||
|
||||
ClientUnitImportResultVO importClientUnits(MultipartFile file);
|
||||
}
|
||||
@@ -0,0 +1,437 @@
|
||||
package com.njcn.gather.aireport.clientunit.service.impl;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
import com.njcn.common.pojo.exception.BusinessException;
|
||||
import com.njcn.gather.aireport.clientunit.component.ClientUnitUserNameResolver;
|
||||
import com.njcn.gather.aireport.clientunit.mapper.ClientUnitMapper;
|
||||
import com.njcn.gather.aireport.clientunit.pojo.constant.ClientUnitConst;
|
||||
import com.njcn.gather.aireport.clientunit.pojo.param.ClientUnitParam;
|
||||
import com.njcn.gather.aireport.clientunit.pojo.po.ClientUnitPO;
|
||||
import com.njcn.gather.aireport.clientunit.pojo.vo.ClientUnitExcelVO;
|
||||
import com.njcn.gather.aireport.clientunit.pojo.vo.ClientUnitImportResultVO;
|
||||
import com.njcn.gather.aireport.clientunit.pojo.vo.ClientUnitVO;
|
||||
import com.njcn.gather.aireport.clientunit.service.IClientUnitService;
|
||||
import com.njcn.gather.system.util.ExportFileNameUtil;
|
||||
import com.njcn.web.factory.PageFactory;
|
||||
import com.njcn.web.utils.ExcelUtil;
|
||||
import com.njcn.web.utils.HttpServletUtil;
|
||||
import com.njcn.web.utils.RequestUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
import org.apache.poi.ss.usermodel.CellStyle;
|
||||
import org.apache.poi.ss.usermodel.DataFormatter;
|
||||
import org.apache.poi.ss.usermodel.Font;
|
||||
import org.apache.poi.ss.usermodel.Row;
|
||||
import org.apache.poi.xssf.usermodel.XSSFSheet;
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URLEncoder;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 委托单位业务实现。
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class ClientUnitServiceImpl extends ServiceImpl<ClientUnitMapper, ClientUnitPO> implements IClientUnitService {
|
||||
|
||||
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
private static final String[] TEMPLATE_HEADERS = new String[]{"单位名称", "联系人", "联系方式", "地址"};
|
||||
|
||||
private final ClientUnitUserNameResolver clientUnitUserNameResolver;
|
||||
|
||||
public ClientUnitServiceImpl(ClientUnitUserNameResolver clientUnitUserNameResolver) {
|
||||
this.clientUnitUserNameResolver = clientUnitUserNameResolver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<ClientUnitVO> listClientUnits(ClientUnitParam.QueryParam param) {
|
||||
ClientUnitParam.QueryParam query = param == null ? new ClientUnitParam.QueryParam() : param;
|
||||
Page<ClientUnitPO> page = this.page(
|
||||
new Page<ClientUnitPO>(PageFactory.getPageNum(query), PageFactory.getPageSize(query)),
|
||||
buildQueryWrapper(query));
|
||||
Page<ClientUnitVO> result = new Page<ClientUnitVO>(page.getCurrent(), page.getSize(), page.getTotal());
|
||||
Map<String, String> userNameMap = resolveUserNameMap(page.getRecords());
|
||||
result.setRecords(page.getRecords().stream()
|
||||
.map(clientUnit -> toVO(clientUnit, userNameMap))
|
||||
.collect(Collectors.toList()));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean addClientUnit(ClientUnitParam.AddParam param) {
|
||||
NormalizedClientUnit data = normalizeParam(param);
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
String userId = resolveCurrentUserId();
|
||||
ClientUnitPO clientUnit = new ClientUnitPO();
|
||||
clientUnit.setId(UUID.randomUUID().toString());
|
||||
clientUnit.setName(data.getName());
|
||||
clientUnit.setContactName(data.getContactName());
|
||||
clientUnit.setPhonenumber(data.getPhonenumber());
|
||||
clientUnit.setAddress(data.getAddress());
|
||||
clientUnit.setStatus(ClientUnitConst.STATUS_NORMAL);
|
||||
clientUnit.setCreateBy(userId);
|
||||
clientUnit.setCreateTime(now);
|
||||
clientUnit.setUpdateBy(userId);
|
||||
clientUnit.setUpdateTime(now);
|
||||
return this.save(clientUnit);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean updateClientUnit(ClientUnitParam.UpdateParam param) {
|
||||
if (param == null || StrUtil.isBlank(param.getId())) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "委托单位ID不能为空");
|
||||
}
|
||||
ClientUnitPO clientUnit = requireNormal(param.getId());
|
||||
NormalizedClientUnit data = normalizeParam(param);
|
||||
checkDuplicate(data.getName(), clientUnit.getId());
|
||||
clientUnit.setName(data.getName());
|
||||
clientUnit.setContactName(data.getContactName());
|
||||
clientUnit.setPhonenumber(data.getPhonenumber());
|
||||
clientUnit.setAddress(data.getAddress());
|
||||
clientUnit.setUpdateBy(resolveCurrentUserId());
|
||||
clientUnit.setUpdateTime(LocalDateTime.now());
|
||||
return this.updateById(clientUnit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientUnitVO getClientUnit(String id) {
|
||||
ClientUnitPO clientUnit = requireNormal(id);
|
||||
return toVO(clientUnit, resolveUserNameMap(Arrays.asList(clientUnit)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean deleteClientUnits(List<String> ids) {
|
||||
List<String> validIds = normalizeIds(ids);
|
||||
if (validIds.isEmpty()) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "委托单位ID不能为空");
|
||||
}
|
||||
return this.lambdaUpdate()
|
||||
.set(ClientUnitPO::getStatus, ClientUnitConst.STATUS_DELETED)
|
||||
.set(ClientUnitPO::getUpdateBy, resolveCurrentUserId())
|
||||
.set(ClientUnitPO::getUpdateTime, LocalDateTime.now())
|
||||
.in(ClientUnitPO::getId, validIds)
|
||||
.eq(ClientUnitPO::getStatus, ClientUnitConst.STATUS_NORMAL)
|
||||
.update();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exportClientUnits(ClientUnitParam.QueryParam param) {
|
||||
List<ClientUnitExcelVO> exportRows = this.list(buildQueryWrapper(param == null ? new ClientUnitParam.QueryParam() : param))
|
||||
.stream()
|
||||
.map(this::toExcelVO)
|
||||
.collect(Collectors.toList());
|
||||
ExcelUtil.exportExcel(ExportFileNameUtil.appendToday("委托单位列表.xlsx"), "委托单位列表", ClientUnitExcelVO.class, exportRows);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void downloadImportTemplate() {
|
||||
HttpServletResponse response = HttpServletUtil.getResponse();
|
||||
try (XSSFWorkbook workbook = new XSSFWorkbook()) {
|
||||
XSSFSheet sheet = workbook.createSheet("委托单位导入模板");
|
||||
writeTemplateHeader(workbook, sheet);
|
||||
String fileName = URLEncoder.encode(ExportFileNameUtil.appendToday("委托单位导入模板.xlsx"), "UTF-8");
|
||||
response.reset();
|
||||
response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
|
||||
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8");
|
||||
try (ServletOutputStream outputStream = response.getOutputStream()) {
|
||||
workbook.write(outputStream);
|
||||
outputStream.flush();
|
||||
}
|
||||
} catch (IOException exception) {
|
||||
log.error("下载委托单位导入模板失败", exception);
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "下载委托单位导入模板失败");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public ClientUnitImportResultVO importClientUnits(MultipartFile file) {
|
||||
if (file == null || file.isEmpty()) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "导入文件不能为空");
|
||||
}
|
||||
List<ClientUnitExcelVO> rows = readImportRows(file);
|
||||
ClientUnitImportResultVO result = new ClientUnitImportResultVO();
|
||||
if (rows.isEmpty()) {
|
||||
result.setFailCount(0);
|
||||
result.setSuccessCount(0);
|
||||
return result;
|
||||
}
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
String userId = resolveCurrentUserId();
|
||||
Set<String> fileKeys = new HashSet<String>();
|
||||
List<ClientUnitPO> saveRows = new ArrayList<ClientUnitPO>();
|
||||
for (int i = 0; i < rows.size(); i++) {
|
||||
int rowNumber = i + 2;
|
||||
try {
|
||||
NormalizedClientUnit data = normalizeExcelRow(rows.get(i));
|
||||
String duplicateKey = data.getName();
|
||||
if (!fileKeys.add(duplicateKey)) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "文件内存在重复委托单位名称");
|
||||
}
|
||||
checkDuplicate(data.getName(), null);
|
||||
ClientUnitPO clientUnit = new ClientUnitPO();
|
||||
clientUnit.setId(UUID.randomUUID().toString());
|
||||
clientUnit.setName(data.getName());
|
||||
clientUnit.setContactName(data.getContactName());
|
||||
clientUnit.setPhonenumber(data.getPhonenumber());
|
||||
clientUnit.setAddress(data.getAddress());
|
||||
clientUnit.setStatus(ClientUnitConst.STATUS_NORMAL);
|
||||
clientUnit.setCreateBy(userId);
|
||||
clientUnit.setCreateTime(now);
|
||||
clientUnit.setUpdateBy(userId);
|
||||
clientUnit.setUpdateTime(now);
|
||||
saveRows.add(clientUnit);
|
||||
result.setSuccessCount(result.getSuccessCount() + 1);
|
||||
} catch (BusinessException exception) {
|
||||
result.setFailCount(result.getFailCount() + 1);
|
||||
result.getFailReasons().add("第" + rowNumber + "行:" + exception.getMessage());
|
||||
}
|
||||
}
|
||||
if (!saveRows.isEmpty()) {
|
||||
this.saveBatch(saveRows);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private LambdaQueryWrapper<ClientUnitPO> buildQueryWrapper(ClientUnitParam.QueryParam query) {
|
||||
LambdaQueryWrapper<ClientUnitPO> wrapper = new LambdaQueryWrapper<ClientUnitPO>();
|
||||
wrapper.eq(ClientUnitPO::getStatus, ClientUnitConst.STATUS_NORMAL)
|
||||
.like(StrUtil.isNotBlank(query.getName()), ClientUnitPO::getName, query.getName());
|
||||
if (StrUtil.isNotBlank(query.getSearchBeginTime())) {
|
||||
wrapper.ge(ClientUnitPO::getCreateTime, parseDateTime(query.getSearchBeginTime(), "开始时间格式不正确"));
|
||||
}
|
||||
if (StrUtil.isNotBlank(query.getSearchEndTime())) {
|
||||
wrapper.le(ClientUnitPO::getCreateTime, parseDateTime(query.getSearchEndTime(), "结束时间格式不正确"));
|
||||
}
|
||||
wrapper.orderByDesc(ClientUnitPO::getCreateTime);
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
private ClientUnitPO requireNormal(String id) {
|
||||
String normalizedId = trimToNull(id);
|
||||
if (normalizedId == null) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "委托单位ID不能为空");
|
||||
}
|
||||
ClientUnitPO clientUnit = this.lambdaQuery()
|
||||
.eq(ClientUnitPO::getId, normalizedId)
|
||||
.eq(ClientUnitPO::getStatus, ClientUnitConst.STATUS_NORMAL)
|
||||
.one();
|
||||
if (clientUnit == null) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "委托单位不存在或已删除");
|
||||
}
|
||||
return clientUnit;
|
||||
}
|
||||
|
||||
private void checkDuplicate(String name, String excludeId) {
|
||||
LambdaQueryWrapper<ClientUnitPO> wrapper = new LambdaQueryWrapper<ClientUnitPO>();
|
||||
wrapper.eq(ClientUnitPO::getName, name)
|
||||
.eq(ClientUnitPO::getStatus, ClientUnitConst.STATUS_NORMAL)
|
||||
.ne(StrUtil.isNotBlank(excludeId), ClientUnitPO::getId, excludeId);
|
||||
if (this.count(wrapper) > 0) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "委托单位名称已存在");
|
||||
}
|
||||
}
|
||||
|
||||
private NormalizedClientUnit normalizeParam(ClientUnitParam.AddParam param) {
|
||||
if (param == null) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "委托单位参数不能为空");
|
||||
}
|
||||
return normalizeFields(param.getName(), param.getContactName(), param.getPhonenumber(), param.getAddress());
|
||||
}
|
||||
|
||||
private NormalizedClientUnit normalizeExcelRow(ClientUnitExcelVO row) {
|
||||
if (row == null) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "空行");
|
||||
}
|
||||
return normalizeFields(row.getName(), row.getContactName(), row.getPhonenumber(), row.getAddress());
|
||||
}
|
||||
|
||||
private NormalizedClientUnit normalizeFields(String name, String contactName, String phonenumber, String address) {
|
||||
NormalizedClientUnit data = new NormalizedClientUnit();
|
||||
data.setName(requireText(name, "单位名称不能为空", 32, "单位名称不能超过32个字符"));
|
||||
data.setContactName(optionalText(contactName, 200, "联系人不能超过200个字符"));
|
||||
data.setPhonenumber(optionalText(phonenumber, 32, "联系方式不能超过32个字符"));
|
||||
data.setAddress(optionalText(address, 128, "地址不能超过128个字符"));
|
||||
return data;
|
||||
}
|
||||
|
||||
private String requireText(String value, String blankMessage, int maxLength, String lengthMessage) {
|
||||
String text = trimToNull(value);
|
||||
if (text == null) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, blankMessage);
|
||||
}
|
||||
if (text.length() > maxLength) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, lengthMessage);
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
private String optionalText(String value, int maxLength, String lengthMessage) {
|
||||
String text = trimToNull(value);
|
||||
if (text != null && text.length() > maxLength) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, lengthMessage);
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
private List<ClientUnitExcelVO> readImportRows(MultipartFile file) {
|
||||
try (InputStream inputStream = file.getInputStream();
|
||||
XSSFWorkbook workbook = new XSSFWorkbook(inputStream)) {
|
||||
XSSFSheet sheet = workbook.getNumberOfSheets() == 0 ? null : workbook.getSheetAt(0);
|
||||
if (sheet == null || sheet.getLastRowNum() < 1) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<ClientUnitExcelVO> rows = new ArrayList<ClientUnitExcelVO>();
|
||||
for (int i = 1; i <= sheet.getLastRowNum(); i++) {
|
||||
Row row = sheet.getRow(i);
|
||||
if (isEmptyRow(row)) {
|
||||
continue;
|
||||
}
|
||||
ClientUnitExcelVO excelVO = new ClientUnitExcelVO();
|
||||
excelVO.setName(readCell(row, 0));
|
||||
excelVO.setContactName(readCell(row, 1));
|
||||
excelVO.setPhonenumber(readCell(row, 2));
|
||||
excelVO.setAddress(readCell(row, 3));
|
||||
rows.add(excelVO);
|
||||
}
|
||||
return rows;
|
||||
} catch (IOException exception) {
|
||||
log.error("读取委托单位导入文件失败", exception);
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "读取导入文件失败");
|
||||
}
|
||||
}
|
||||
|
||||
private void writeTemplateHeader(XSSFWorkbook workbook, XSSFSheet sheet) {
|
||||
CellStyle style = workbook.createCellStyle();
|
||||
Font font = workbook.createFont();
|
||||
font.setBold(true);
|
||||
style.setFont(font);
|
||||
Row row = sheet.createRow(0);
|
||||
for (int i = 0; i < TEMPLATE_HEADERS.length; i++) {
|
||||
Cell cell = row.createCell(i);
|
||||
cell.setCellValue(TEMPLATE_HEADERS[i]);
|
||||
cell.setCellStyle(style);
|
||||
sheet.setColumnWidth(i, 25 * 256);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isEmptyRow(Row row) {
|
||||
if (row == null) {
|
||||
return true;
|
||||
}
|
||||
for (int i = 0; i < TEMPLATE_HEADERS.length; i++) {
|
||||
if (trimToNull(readCell(row, i)) != null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private String readCell(Row row, int index) {
|
||||
Cell cell = row.getCell(index);
|
||||
if (cell == null) {
|
||||
return null;
|
||||
}
|
||||
return trimToNull(new DataFormatter().formatCellValue(cell));
|
||||
}
|
||||
|
||||
private List<String> normalizeIds(List<String> ids) {
|
||||
if (ids == null || ids.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<String> result = new ArrayList<String>();
|
||||
for (String id : ids) {
|
||||
String normalizedId = trimToNull(id);
|
||||
if (normalizedId != null && !result.contains(normalizedId)) {
|
||||
result.add(normalizedId);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private LocalDateTime parseDateTime(String value, String errorMessage) {
|
||||
try {
|
||||
return LocalDateTime.parse(value.trim(), DATE_TIME_FORMATTER);
|
||||
} catch (DateTimeParseException exception) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
private ClientUnitVO toVO(ClientUnitPO clientUnit, Map<String, String> userNameMap) {
|
||||
ClientUnitVO vo = new ClientUnitVO();
|
||||
BeanUtil.copyProperties(clientUnit, vo);
|
||||
vo.setCreateBy(resolveUserName(clientUnit.getCreateBy(), userNameMap));
|
||||
vo.setUpdateBy(resolveUserName(clientUnit.getUpdateBy(), userNameMap));
|
||||
return vo;
|
||||
}
|
||||
|
||||
private ClientUnitExcelVO toExcelVO(ClientUnitPO clientUnit) {
|
||||
ClientUnitExcelVO vo = new ClientUnitExcelVO();
|
||||
BeanUtil.copyProperties(clientUnit, vo);
|
||||
return vo;
|
||||
}
|
||||
|
||||
private Map<String, String> resolveUserNameMap(List<ClientUnitPO> clientUnits) {
|
||||
List<String> userIds = clientUnits.stream()
|
||||
.flatMap(clientUnit -> Arrays.asList(clientUnit.getCreateBy(), clientUnit.getUpdateBy()).stream())
|
||||
.filter(StrUtil::isNotBlank)
|
||||
.collect(Collectors.toList());
|
||||
return clientUnitUserNameResolver.resolveUserNames(userIds);
|
||||
}
|
||||
|
||||
private String resolveUserName(String userId, Map<String, String> userNameMap) {
|
||||
if (StrUtil.isBlank(userId)) {
|
||||
return null;
|
||||
}
|
||||
String userName = userNameMap == null ? null : userNameMap.get(userId);
|
||||
return StrUtil.isNotBlank(userName) ? userName : userId;
|
||||
}
|
||||
|
||||
private String resolveCurrentUserId() {
|
||||
String userId = RequestUtil.getUserId();
|
||||
return StrUtil.isBlank(userId) ? null : userId;
|
||||
}
|
||||
|
||||
private String trimToNull(String value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
String text = value.trim();
|
||||
return text.isEmpty() ? null : text;
|
||||
}
|
||||
|
||||
@lombok.Data
|
||||
private static class NormalizedClientUnit {
|
||||
private String name;
|
||||
private String contactName;
|
||||
private String phonenumber;
|
||||
private String address;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
<?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.njcn.gather.aireport.clientunit.mapper.ClientUnitMapper">
|
||||
|
||||
</mapper>
|
||||
Reference in New Issue
Block a user