目录
1、业务场景
(1)、分享原因
(2)、需求分析
(3)、数据库设计
2、开发环境和工具
3、代码思路
4、参考代码
导出模板vo(KitchenStoreExcelVo)
商铺信息表(HwKitchenStoreInfo)
Mapper 接口(HwKitchenStoreInfoMapper)
Mapping.xml(HwKitchenStoreInfoMapper.xml)
controller层(KitchenStoreManageController)
service层(IKitchenStoreManageService)
serviceImpl层(KitchenStoreManageServiceImpl)
5、总结
之前笔者分享过导出功能的一种写法,既然有导出功能,那相对的导入功能也是存在的一种需求,本期笔者就分享一下导入功能的一种思路;
导入功能可分为两个子需求,子需求1是下载导入模板,子需求2是导入模板数据;
本次代码演示使用的业务背景是商铺基本信息管理,预插入数据对应的数据库设计如下图所示:
餐厨商铺信息表
因为该业务功能是在我实习公司已有项目下的拓展,所以环境依赖较多,我将重点阐述我所负责的后端代码逻辑,开发环境和工具只会简单带过;如果在借鉴代码过程中缺失包或者依赖,请根据IDE提示或者自行百度调试(就当锻炼自己对开发工具的熟悉程度啦);
(1)、IDE:IntelliJ IDEA 2022.1
(2)、测试:Postman
(3)、JDK:jdk-11.0.15
(4)、Spring Boot Version:2.2.5.RELEASE
(5)、modelVersion:4.0.0
(6)、pomVersion:0.0.1-SNAPSHOT
本次导入功能以最简单的(商铺)管理为代码背景来进行展示和说明:
代码说明:
(1)、@Excel里面需要声明三个东西:
name:列名,对应数据库需要的字段;必填还是选填取决于数据库对应的字段能否为空,可以添加适当备注引导使用者正确填写;注意!并不是所有的数据库字段都需要用户导入,这里选择字段时参照的标准是对应新增功能需要用户填写的信息,所以本质是一个变相的新增功能,只是可以达到批量新增的效果而已;
orderNum:第几列,从0(这里的0等价于第1列)开始;
width:列宽,长度定义取决于你的列名(name)的长度,自行调试感受就行;
(2)、其他就是Vo的基本写法了,定义好属性后idea可以一键生成get和set方法,只需要右键空白处然后点击弹出菜单的Generate选择Getter and Setter,最后全选对应的属性即可;
package xxx.vo;
import cn.afterturn.easypoi.excel.annotation.Excel;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.util.Date;
/**
*
* 商铺信息导入excel模板下载用vo
*
*
* @author Mike-GY
* @since 2022-08-29
*/
public class KitchenStoreExcelVo {
/**
* 商铺名称
*/
@Excel(name = "商铺名称(必填,不能重复)", orderNum = "0", width = 30)
private String storeName;
/**
* 商铺负责人名称
*/
@Excel(name = "负责人(选填)", orderNum = "1", width = 20)
private String leader;
/**
* 商铺负责人电话
*/
@Excel(name = "联系电话(选填)", orderNum = "2", width = 20)
private String leaderPhone;
/**
* 商铺类型名称
*/
@Excel(name = "商铺类型(必填,请按照新增商铺功能下商铺类型可选选项来填写)", orderNum = "3", width = 60)
private String typeName;
/**
* 所属政府区域名称
*/
@Excel(name = "所属组织(必填,请按照新增商铺功能下所属组织可选选项来填写)", orderNum = "4", width = 60)
private String depName;
/**
* 使用的垃圾桶数量
*/
@Excel(name = "容器数量(个)(选填)", orderNum = "5", width = 25)
private Integer garbageCanNum;
/**
* 商铺地址
*/
@Excel(name = "地址(选填)", orderNum = "6", width = 20)
private String storeAddress;
/**
* 营业状态名称
*/
@Excel(name = "营业状态(选填)", orderNum = "7", width = 20)
private String storeStatusName;
/**
* 签约时间
*/
@Excel(name = "签约时间(选填,时间格式:yyyy-MM-dd)", orderNum = "8", width = 40)
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GTM+8")
private Date signTime;
public String getStoreName() {
return storeName;
}
public void setStoreName(String storeName) {
this.storeName = storeName;
}
public String getLeader() {
return leader;
}
public void setLeader(String leader) {
this.leader = leader;
}
public String getLeaderPhone() {
return leaderPhone;
}
public void setLeaderPhone(String leaderPhone) {
this.leaderPhone = leaderPhone;
}
public String getTypeName() {
return typeName;
}
public void setTypeName(String typeName) {
this.typeName = typeName;
}
public String getDepName() {
return depName;
}
public void setDepName(String depName) {
this.depName = depName;
}
public Integer getGarbageCanNum() {
return garbageCanNum;
}
public void setGarbageCanNum(Integer garbageCanNum) {
this.garbageCanNum = garbageCanNum;
}
public String getStoreAddress() {
return storeAddress;
}
public void setStoreAddress(String storeAddress) {
this.storeAddress = storeAddress;
}
public String getStoreStatusName() {
return storeStatusName;
}
public void setStoreStatusName(String storeStatusName) {
this.storeStatusName = storeStatusName;
}
public Date getSignTime() {
return signTime;
}
public void setSignTime(Date signTime) {
this.signTime = signTime;
}
}
该model对应的是之前提到过的预插入数据对应的数据库的model,我是使用代码生成器快捷生成的,只能借鉴个结构,最上方的注入估计读者们抄不了,因为依赖存放的包位置绝对不一样,请自行查找补充;
package xxx.model;
import com.baomidou.mybatisplus.enums.IdType;
import java.util.Date;
import com.baomidou.mybatisplus.annotations.TableId;
import com.baomidou.mybatisplus.annotations.TableField;
import com.baomidou.mybatisplus.annotations.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serializable;
/**
*
* 餐厨商铺信息表
*
*
* @author Mike-GY
* @since 2022-08-26
*/
@TableName("hw_kitchen_store_info")
public class HwKitchenStoreInfo implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
/**
* 商铺名称
*/
@TableField("store_name")
private String storeName;
/**
* 商铺负责人名称
*/
private String leader;
/**
* 商铺负责人电话
*/
@TableField("leader_phone")
private String leaderPhone;
/**
* 商铺类型编码(来源于字典表)
*/
@TableField("type_code")
private String typeCode;
/**
* 商铺类型名称
*/
@TableField("type_name")
private String typeName;
/**
* 所属车组或部门id
*/
@TableField("dep_id")
private Integer depId;
/**
* 所属车组或部门名称
*/
@TableField("dep_name")
private String depName;
/**
* 使用的垃圾桶数量
*/
@TableField("garbage_can_num")
private Integer garbageCanNum;
/**
* 经度
*/
private Double longitude;
/**
* 纬度
*/
private Double latitude;
/**
* 商铺地址
*/
@TableField("store_address")
private String storeAddress;
/**
* 营业状态编码(来源于字典表)
*/
@TableField("store_status_code")
private String storeStatusCode;
/**
* 营业状态名称
*/
@TableField("store_status_name")
private String storeStatusName;
/**
* 签约时间
*/
@TableField("sign_time")
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GTM+8")
private Date signTime;
/**
* 删除标识:0:未删除,1:已删除;
*/
private Integer deleted;
/**
* 创建时间
*/
@TableField("create_time")
private Date createTime;
/**
* 更新时间
*/
@TableField("update_time")
private Date updateTime;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getStoreName() {
return storeName;
}
public void setStoreName(String storeName) {
this.storeName = storeName;
}
public String getLeader() {
return leader;
}
public void setLeader(String leader) {
this.leader = leader;
}
public String getLeaderPhone() {
return leaderPhone;
}
public void setLeaderPhone(String leaderPhone) {
this.leaderPhone = leaderPhone;
}
public String getTypeCode() {
return typeCode;
}
public void setTypeCode(String typeCode) {
this.typeCode = typeCode;
}
public String getTypeName() {
return typeName;
}
public void setTypeName(String typeName) {
this.typeName = typeName;
}
public Integer getDepId() {
return depId;
}
public void setDepId(Integer depId) {
this.depId = depId;
}
public String getDepName() {
return depName;
}
public void setDepName(String depName) {
this.depName = depName;
}
public Integer getGarbageCanNum() {
return garbageCanNum;
}
public void setGarbageCanNum(Integer garbageCanNum) {
this.garbageCanNum = garbageCanNum;
}
public Double getLongitude() {
return longitude;
}
public void setLongitude(Double longitude) {
this.longitude = longitude;
}
public Double getLatitude() {
return latitude;
}
public void setLatitude(Double latitude) {
this.latitude = latitude;
}
public String getStoreAddress() {
return storeAddress;
}
public void setStoreAddress(String storeAddress) {
this.storeAddress = storeAddress;
}
public String getStoreStatusCode() {
return storeStatusCode;
}
public void setStoreStatusCode(String storeStatusCode) {
this.storeStatusCode = storeStatusCode;
}
public String getStoreStatusName() {
return storeStatusName;
}
public void setStoreStatusName(String storeStatusName) {
this.storeStatusName = storeStatusName;
}
public Date getSignTime() {
return signTime;
}
public void setSignTime(Date signTime) {
this.signTime = signTime;
}
public Integer getDeleted() {
return deleted;
}
public void setDeleted(Integer deleted) {
this.deleted = deleted;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public Date getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
@Override
public String toString() {
return "HwKitchenStoreInfo{" +
"id=" + id +
", storeName=" + storeName +
", leader=" + leader +
", leaderPhone=" + leaderPhone +
", typeCode=" + typeCode +
", typeName=" + typeName +
", depId=" + depId +
", depName=" + depName +
", garbageCanNum=" + garbageCanNum +
", longitude=" + longitude +
", latitude=" + latitude +
", storeAddress=" + storeAddress +
", storeStatusCode=" + storeStatusCode +
", storeStatusName=" + storeStatusName +
", signTime=" + signTime +
", deleted=" + deleted +
", createTime=" + createTime +
", updateTime=" + updateTime +
"}";
}
}
package xxx.mapper;
import xxx.entity.model.HwKitchenStoreInfo;
import com.baomidou.mybatisplus.mapper.BaseMapper;
import org.springframework.stereotype.Component;
/**
*
* 餐厨商铺信息表 Mapper 接口
*
*
* @author Mike-GY
* @since 2022-08-26
*/
@Component
public interface HwKitchenStoreInfoMapper extends BaseMapper {
}
id, store_name, leader, leader_phone, type_code, type_name, dep_id, dep_name, garbage_can_num, longitude, latitude, store_address, store_status_code, store_status_name, sign_time, deleted, create_time, update_time
package xxx.kitchen.controller;
import xxx.exception.BusinessException;
import xxx.utils.message.Messages;
import xxx.utils.util.CommonResponse;
import xxx.utils.util.MessagesUtil;
import xxx.mc.utils.FileWithExcelUtil;
import xxx.kitchen.service.IKitchenStoreManageService;
import xxx.kitchen.vo.KitchenStoreExcelVo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @author Mike-GY
*/
@RestController
@RequestMapping("/kitchen/KitchenStoreManage")
public class KitchenStoreManageController {
// 依赖注入
private final Logger logger = LoggerFactory.getLogger(getClass());
private final IKitchenStoreManageService iKitchenStoreManageService;
public KitchenStoreManageController(IKitchenStoreManageService iKitchenStoreManageService) {
this.iKitchenStoreManageService = iKitchenStoreManageService;
}
/**
* 导入商铺信息——下载导入模板
* @param response http响应
* @author Mike-GY
* @since 2022/8/29
*/
@PostMapping("/exportStoreInfoExcel")
public void exportStoreInfoExcel(HttpServletResponse response) {
try {
// 将excelVo里的属性复制成一个list
List excelVoList = new ArrayList<>();
// 将list通过FileWithExcelUtil.exportExcel导出成自定义的excel文件 FileWithExcelUtil.exportExcel(excelVoList,null,null,KitchenStoreExcelVo.class, "商铺信息导入模板.xls",response);
// 返回提示信息
logger.info("商铺信息导入模板下载成功");
} catch (Exception e) {
logger.error("商铺信息导入模板下载失败,错误原因为:" + e.getMessage());
}
}
/**
* 导入商铺信息——导入模板数据
* @param file 导入模板文件
* @return res
* @author Mike-GY
* @since 2022/8/29
*/
@PostMapping("/importStoreInfoExcel")
public CommonResponse importStoreInfoExcel(@RequestBody MultipartFile file) throws Exception {
CommonResponse res = CommonResponse.getInstance();
// 对文件进行判空处理
if (file == null ) {
throw new BusinessException(Messages.CODE_500202, MessagesUtil.getMessage(Messages.CODE_500202));
}
// 如果文件不为空则调用导入服务
Map result = iKitchenStoreManageService.importStoreInfoExcel(file);
// 返回导入服务调用结果
res.setResultData(result);
return res;
}
}
package xxx.kitchen.service;
import org.springframework.web.multipart.MultipartFile;
import java.util.Map;
/**
* @author Mike-GY
*/
public interface IKitchenStoreManageService {
/**
* 导入商铺信息——导入模板数据
* @param file 导入模板文件
* @return res
* @throws Exception 抛出异常
*/
Map importStoreInfoExcel(MultipartFile file) throws Exception;
}
package xxx.kitchen.service.impl;
import xxx.utils.util.ExcelUtil;
import xxx.entity.CodeMaster;
import xxx.entity.DepartmentInfo;
import xxx.entity.mapper.HwKitchenStoreInfoMapper;
import xxx.entity.model.HwKitchenStoreInfo;
import xxx.mapper.CodeMasterMapper;
import xxx.mapper.DepartmentInfoMapper;
import xxx.kitchen.service.IKitchenStoreManageService;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* @author Mike-GY
*/
@Service
public class KitchenStoreManageServiceImpl implements IKitchenStoreManageService {
// 注入需要的mapper
private final HwKitchenStoreInfoMapper hwKitchenStoreInfoMapper;
private final CodeMasterMapper codeMasterMapper ;
private final DepartmentInfoMapper departmentInfoMapper;
public KitchenStoreManageServiceImpl(
HwKitchenStoreInfoMapper hwKitchenStoreInfoMapper,
CodeMasterMapper codeMasterMapper,
DepartmentInfoMapper departmentInfoMapper
) {
this.hwKitchenStoreInfoMapper = hwKitchenStoreInfoMapper;
this.codeMasterMapper = codeMasterMapper;
this.departmentInfoMapper = departmentInfoMapper;
}
/**
* 导入商铺信息——导入模板数据
*/
@Override
public Map importStoreInfoExcel(MultipartFile file) throws Exception {
// 定义一个HashMap来存放导入结果
Map result = new HashMap(8);
// 定义一个list来存放导入失败的数据
List
1、需要的包和依赖需要自行引入;
2、个人理解:让用户填写的信息越简单越好,数据库的设计(字段覆盖面)越完整越好,那导入的时候如何补全两者之间的差异呢?如果是软件上的新增商铺功能,可以让前端绑定传参,比如新增商铺时用户选择了某个商铺所属部门,前端会将部门id一并传给你,你设置一个参数接收它就行;
3、而如果你想用文件,比如说excel来导入的话,则需要后端自行根据传入部门名称去部门这张表做查询并赋值到对应的商铺信息表,可以学我在servicelmpl里另写几个私有函数完成这个数据处理的过程;