数据导入
This commit is contained in:
parent
4ecf121014
commit
4531e111d6
|
|
@ -3,15 +3,9 @@ package com.ruoyi.business.ajax;
|
||||||
import com.alibaba.excel.EasyExcel;
|
import com.alibaba.excel.EasyExcel;
|
||||||
import com.alibaba.excel.ExcelReader;
|
import com.alibaba.excel.ExcelReader;
|
||||||
import com.ruoyi.business.domain.BizAccount;
|
import com.ruoyi.business.domain.BizAccount;
|
||||||
import com.ruoyi.business.mapper.BizAccountMapper;
|
import com.ruoyi.business.mapper.*;
|
||||||
import com.ruoyi.business.mapper.BizMemberMapper;
|
|
||||||
import com.ruoyi.business.mapper.BizProductMapper;
|
|
||||||
import com.ruoyi.business.mapper.BizProductTypeMapper;
|
|
||||||
import com.ruoyi.business.service.IBizMemberService;
|
import com.ruoyi.business.service.IBizMemberService;
|
||||||
import com.ruoyi.business.sync.GoodsData;
|
import com.ruoyi.business.sync.*;
|
||||||
import com.ruoyi.business.sync.GoodsDataListener;
|
|
||||||
import com.ruoyi.business.sync.UserData;
|
|
||||||
import com.ruoyi.business.sync.UserDataListener;
|
|
||||||
import com.ruoyi.common.core.controller.BaseController;
|
import com.ruoyi.common.core.controller.BaseController;
|
||||||
import com.ruoyi.common.core.domain.AjaxResult;
|
import com.ruoyi.common.core.domain.AjaxResult;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
|
@ -35,6 +29,10 @@ public class SyncDataController extends BaseController {
|
||||||
private BizProductMapper productMapper;
|
private BizProductMapper productMapper;
|
||||||
@Resource
|
@Resource
|
||||||
private BizProductTypeMapper productTypeMapper;
|
private BizProductTypeMapper productTypeMapper;
|
||||||
|
@Resource
|
||||||
|
private BizMemberAddressMapper memberAddressMapper;
|
||||||
|
@Resource
|
||||||
|
private BizOrderMapper orderMapper;
|
||||||
|
|
||||||
@PostMapping("/user")
|
@PostMapping("/user")
|
||||||
public AjaxResult user(@RequestParam("file") MultipartFile file) {
|
public AjaxResult user(@RequestParam("file") MultipartFile file) {
|
||||||
|
|
@ -66,6 +64,36 @@ public class SyncDataController extends BaseController {
|
||||||
return AjaxResult.success();
|
return AjaxResult.success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("/memberAddress")
|
||||||
|
public AjaxResult memberAddress(@RequestParam("file") MultipartFile file) {
|
||||||
|
ExcelReader reader = null;
|
||||||
|
try {
|
||||||
|
reader = EasyExcel.read(file.getInputStream(), UserAddressData.class, new UserAddressDataListener(memberAddressMapper, memberMapper)).build();
|
||||||
|
reader.readAll();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} finally {
|
||||||
|
assert reader != null;
|
||||||
|
reader.finish();
|
||||||
|
}
|
||||||
|
return AjaxResult.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/order")
|
||||||
|
public AjaxResult order(@RequestParam("file") MultipartFile file) {
|
||||||
|
ExcelReader reader = null;
|
||||||
|
try {
|
||||||
|
reader = EasyExcel.read(file.getInputStream(), OrderData.class, new OrderDataListener(orderMapper, memberMapper)).build();
|
||||||
|
reader.readAll();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} finally {
|
||||||
|
assert reader != null;
|
||||||
|
reader.finish();
|
||||||
|
}
|
||||||
|
return AjaxResult.success();
|
||||||
|
}
|
||||||
|
|
||||||
@PostMapping("/initUserTree")
|
@PostMapping("/initUserTree")
|
||||||
public AjaxResult initUserTree() {
|
public AjaxResult initUserTree() {
|
||||||
return AjaxResult.success();
|
return AjaxResult.success();
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,9 @@ public class BizMemberAddress extends BaseEntity
|
||||||
@Excel(name = "是否删除:0-否,1-是")
|
@Excel(name = "是否删除:0-否,1-是")
|
||||||
private Integer isDelete;
|
private Integer isDelete;
|
||||||
|
|
||||||
|
/** 是否默认:0-否,1-是 */
|
||||||
|
private Integer isDefault;
|
||||||
|
|
||||||
public void setId(Long id)
|
public void setId(Long id)
|
||||||
{
|
{
|
||||||
this.id = id;
|
this.id = id;
|
||||||
|
|
@ -170,6 +173,14 @@ public class BizMemberAddress extends BaseEntity
|
||||||
return isDelete;
|
return isDelete;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Integer getIsDefault() {
|
||||||
|
return isDefault;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIsDefault(Integer isDefault) {
|
||||||
|
this.isDefault = isDefault;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
|
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
|
||||||
|
|
@ -184,6 +195,7 @@ public class BizMemberAddress extends BaseEntity
|
||||||
.append("areaCode", getAreaCode())
|
.append("areaCode", getAreaCode())
|
||||||
.append("areaName", getAreaName())
|
.append("areaName", getAreaName())
|
||||||
.append("isDelete", getIsDelete())
|
.append("isDelete", getIsDelete())
|
||||||
|
.append("isDefault", getIsDefault())
|
||||||
.append("createBy", getCreateBy())
|
.append("createBy", getCreateBy())
|
||||||
.append("createTime", getCreateTime())
|
.append("createTime", getCreateTime())
|
||||||
.append("updateBy", getUpdateBy())
|
.append("updateBy", getUpdateBy())
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,158 @@
|
||||||
|
package com.ruoyi.business.sync;
|
||||||
|
|
||||||
|
public class OrderData {
|
||||||
|
|
||||||
|
private String id;
|
||||||
|
private String orderNumber;
|
||||||
|
private String userId;
|
||||||
|
private String goodsId;
|
||||||
|
private String addressId;
|
||||||
|
private String goodsUnitPrice;
|
||||||
|
private String buyGoodsNums;
|
||||||
|
private String payCount;
|
||||||
|
private String orderStatus;
|
||||||
|
private String usePorintsType;
|
||||||
|
private String payMethod;
|
||||||
|
private String goodsFreight;
|
||||||
|
private String orderRemark;
|
||||||
|
private String addtime;
|
||||||
|
private String detailsAddress;
|
||||||
|
private String addID;
|
||||||
|
private String mobile;
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOrderNumber() {
|
||||||
|
return orderNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOrderNumber(String orderNumber) {
|
||||||
|
this.orderNumber = orderNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUserId() {
|
||||||
|
return userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserId(String userId) {
|
||||||
|
this.userId = userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getGoodsId() {
|
||||||
|
return goodsId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGoodsId(String goodsId) {
|
||||||
|
this.goodsId = goodsId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAddressId() {
|
||||||
|
return addressId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAddressId(String addressId) {
|
||||||
|
this.addressId = addressId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getGoodsUnitPrice() {
|
||||||
|
return goodsUnitPrice;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGoodsUnitPrice(String goodsUnitPrice) {
|
||||||
|
this.goodsUnitPrice = goodsUnitPrice;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBuyGoodsNums() {
|
||||||
|
return buyGoodsNums;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBuyGoodsNums(String buyGoodsNums) {
|
||||||
|
this.buyGoodsNums = buyGoodsNums;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPayCount() {
|
||||||
|
return payCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPayCount(String payCount) {
|
||||||
|
this.payCount = payCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOrderStatus() {
|
||||||
|
return orderStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOrderStatus(String orderStatus) {
|
||||||
|
this.orderStatus = orderStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUsePorintsType() {
|
||||||
|
return usePorintsType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUsePorintsType(String usePorintsType) {
|
||||||
|
this.usePorintsType = usePorintsType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPayMethod() {
|
||||||
|
return payMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPayMethod(String payMethod) {
|
||||||
|
this.payMethod = payMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getGoodsFreight() {
|
||||||
|
return goodsFreight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGoodsFreight(String goodsFreight) {
|
||||||
|
this.goodsFreight = goodsFreight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOrderRemark() {
|
||||||
|
return orderRemark;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOrderRemark(String orderRemark) {
|
||||||
|
this.orderRemark = orderRemark;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAddtime() {
|
||||||
|
return addtime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAddtime(String addtime) {
|
||||||
|
this.addtime = addtime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDetailsAddress() {
|
||||||
|
return detailsAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDetailsAddress(String detailsAddress) {
|
||||||
|
this.detailsAddress = detailsAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAddID() {
|
||||||
|
return addID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAddID(String addID) {
|
||||||
|
this.addID = addID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMobile() {
|
||||||
|
return mobile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMobile(String mobile) {
|
||||||
|
this.mobile = mobile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,70 @@
|
||||||
|
package com.ruoyi.business.sync;
|
||||||
|
|
||||||
|
import com.alibaba.excel.context.AnalysisContext;
|
||||||
|
import com.alibaba.excel.event.AnalysisEventListener;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.ruoyi.business.domain.*;
|
||||||
|
import com.ruoyi.business.mapper.BizMemberMapper;
|
||||||
|
import com.ruoyi.business.mapper.BizOrderMapper;
|
||||||
|
import com.ruoyi.business.mapper.BizProductMapper;
|
||||||
|
import com.ruoyi.business.mapper.BizProductTypeMapper;
|
||||||
|
import com.ruoyi.common.utils.DateUtils;
|
||||||
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public class OrderDataListener extends AnalysisEventListener<OrderData> {
|
||||||
|
|
||||||
|
private BizOrderMapper orderMapper;
|
||||||
|
|
||||||
|
private BizMemberMapper memberMapper;
|
||||||
|
|
||||||
|
public OrderDataListener(BizOrderMapper orderMapper, BizMemberMapper memberMapper) {
|
||||||
|
this.orderMapper = orderMapper;
|
||||||
|
this.memberMapper = memberMapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invoke(OrderData orderData, AnalysisContext analysisContext) {
|
||||||
|
BizOrder order = new BizOrder();
|
||||||
|
order.setId(Long.valueOf(orderData.getId()));
|
||||||
|
order.setOrderSn(orderData.getOrderNumber());
|
||||||
|
order.setMobile(orderData.getMobile());
|
||||||
|
order.setOrderAmount(new BigDecimal(orderData.getPayCount()));
|
||||||
|
order.setOrderStatus(Integer.parseInt(orderData.getOrderStatus()));
|
||||||
|
order.setCreateBy("admin");
|
||||||
|
order.setCreateTime(DateUtils.parseDate(orderData.getAddtime()));
|
||||||
|
order.setAddressId(StringUtils.isBlank(orderData.getAddID()) ? 0L : Long.parseLong(orderData.getAddID()));
|
||||||
|
order.setAddressDetail(orderData.getDetailsAddress());
|
||||||
|
order.setRemark(orderData.getOrderRemark());
|
||||||
|
|
||||||
|
BizMember member = memberMapper.selectBizMemberByMobile(order.getMobile());
|
||||||
|
if (Objects.isNull(member)) {
|
||||||
|
order.setMemberId(0L);
|
||||||
|
order.setMemberName("");
|
||||||
|
} else {
|
||||||
|
order.setMemberId(member.getId());
|
||||||
|
order.setMemberName(member.getMemberName());
|
||||||
|
}
|
||||||
|
orderMapper.insertBizOrder(order);
|
||||||
|
|
||||||
|
BizOrderDetail orderDetail = new BizOrderDetail();
|
||||||
|
orderDetail.setOrderId(order.getId());
|
||||||
|
orderDetail.setOrderSn(order.getOrderSn());
|
||||||
|
orderDetail.setProductAmount(new BigDecimal(orderData.getGoodsUnitPrice()));
|
||||||
|
orderDetail.setProductCode(orderData.getGoodsId());
|
||||||
|
orderDetail.setProductCount(Integer.parseInt(orderData.getBuyGoodsNums()));
|
||||||
|
orderDetail.setCreateBy("admin");
|
||||||
|
orderDetail.setCreateTime(order.getCreateTime());
|
||||||
|
orderDetail.setProductId(0L);
|
||||||
|
orderMapper.insertBizOrderDetail(orderDetail);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,122 @@
|
||||||
|
package com.ruoyi.business.sync;
|
||||||
|
|
||||||
|
public class UserAddressData {
|
||||||
|
|
||||||
|
private String id;
|
||||||
|
private String addressId;
|
||||||
|
private String userId;
|
||||||
|
private String receiveUser;
|
||||||
|
private String receiveTelephone;
|
||||||
|
private String receiveAreaCode;
|
||||||
|
private String receiveAreaName;
|
||||||
|
private String receiveCityName;
|
||||||
|
private String receiveCityCode;
|
||||||
|
private String receiveProvinceName;
|
||||||
|
private String receiveProvinceCode;
|
||||||
|
private String detailsAddress;
|
||||||
|
private String isDefalut;
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAddressId() {
|
||||||
|
return addressId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAddressId(String addressId) {
|
||||||
|
this.addressId = addressId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUserId() {
|
||||||
|
return userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserId(String userId) {
|
||||||
|
this.userId = userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getReceiveUser() {
|
||||||
|
return receiveUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setReceiveUser(String receiveUser) {
|
||||||
|
this.receiveUser = receiveUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getReceiveTelephone() {
|
||||||
|
return receiveTelephone;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setReceiveTelephone(String receiveTelephone) {
|
||||||
|
this.receiveTelephone = receiveTelephone;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getReceiveAreaCode() {
|
||||||
|
return receiveAreaCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setReceiveAreaCode(String receiveAreaCode) {
|
||||||
|
this.receiveAreaCode = receiveAreaCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getReceiveAreaName() {
|
||||||
|
return receiveAreaName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setReceiveAreaName(String receiveAreaName) {
|
||||||
|
this.receiveAreaName = receiveAreaName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getReceiveCityName() {
|
||||||
|
return receiveCityName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setReceiveCityName(String receiveCityName) {
|
||||||
|
this.receiveCityName = receiveCityName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getReceiveCityCode() {
|
||||||
|
return receiveCityCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setReceiveCityCode(String receiveCityCode) {
|
||||||
|
this.receiveCityCode = receiveCityCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getReceiveProvinceName() {
|
||||||
|
return receiveProvinceName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setReceiveProvinceName(String receiveProvinceName) {
|
||||||
|
this.receiveProvinceName = receiveProvinceName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getReceiveProvinceCode() {
|
||||||
|
return receiveProvinceCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setReceiveProvinceCode(String receiveProvinceCode) {
|
||||||
|
this.receiveProvinceCode = receiveProvinceCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDetailsAddress() {
|
||||||
|
return detailsAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDetailsAddress(String detailsAddress) {
|
||||||
|
this.detailsAddress = detailsAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIsDefalut() {
|
||||||
|
return isDefalut;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIsDefalut(String isDefalut) {
|
||||||
|
this.isDefalut = isDefalut;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
package com.ruoyi.business.sync;
|
||||||
|
|
||||||
|
import com.alibaba.excel.context.AnalysisContext;
|
||||||
|
import com.alibaba.excel.event.AnalysisEventListener;
|
||||||
|
import com.ruoyi.business.domain.BizMember;
|
||||||
|
import com.ruoyi.business.domain.BizMemberAddress;
|
||||||
|
import com.ruoyi.business.mapper.BizMemberAddressMapper;
|
||||||
|
import com.ruoyi.business.mapper.BizMemberMapper;
|
||||||
|
import com.ruoyi.common.utils.DateUtils;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public class UserAddressDataListener extends AnalysisEventListener<UserAddressData> {
|
||||||
|
|
||||||
|
private BizMemberAddressMapper memberAddressMapper;
|
||||||
|
|
||||||
|
private BizMemberMapper memberMapper;
|
||||||
|
|
||||||
|
public UserAddressDataListener(BizMemberAddressMapper memberAddressMapper, BizMemberMapper memberMapper) {
|
||||||
|
this.memberAddressMapper = memberAddressMapper;
|
||||||
|
this.memberMapper = memberMapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invoke(UserAddressData userAddressData, AnalysisContext analysisContext) {
|
||||||
|
BizMemberAddress memberAddress = new BizMemberAddress();
|
||||||
|
memberAddress.setId(Long.valueOf(userAddressData.getId()));
|
||||||
|
memberAddress.setAddress(userAddressData.getDetailsAddress());
|
||||||
|
memberAddress.setProvinceCode(userAddressData.getReceiveProvinceCode());
|
||||||
|
memberAddress.setProvinceName(userAddressData.getReceiveProvinceName());
|
||||||
|
memberAddress.setCityCode(userAddressData.getReceiveCityCode());
|
||||||
|
memberAddress.setCityName(userAddressData.getReceiveCityName());
|
||||||
|
memberAddress.setAreaCode(userAddressData.getReceiveAreaCode());
|
||||||
|
memberAddress.setAreaName(userAddressData.getReceiveAreaName());
|
||||||
|
memberAddress.setIsDefault("Y".equals(userAddressData.getIsDefalut()) ? 1 : 0);
|
||||||
|
memberAddress.setIsDelete(0);
|
||||||
|
memberAddress.setMobile(userAddressData.getReceiveTelephone());
|
||||||
|
memberAddress.setMemberName(userAddressData.getReceiveUser());
|
||||||
|
memberAddress.setCreateBy("admin");
|
||||||
|
memberAddress.setCreateTime(DateUtils.getNowDate());
|
||||||
|
|
||||||
|
BizMember member = memberMapper.selectBizMemberByMobile(userAddressData.getReceiveTelephone());
|
||||||
|
if (!Objects.isNull(member)) {
|
||||||
|
memberAddress.setMemberID(member.getId());
|
||||||
|
} else {
|
||||||
|
memberAddress.setMemberID(0L);
|
||||||
|
}
|
||||||
|
memberAddressMapper.insertBizMemberAddress(memberAddress);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue