package com.doumee.service.business.impl;
|
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.doumee.core.constants.Constants;
|
import com.doumee.core.constants.ResponseStatus;
|
import com.doumee.core.exception.BusinessException;
|
import com.doumee.core.model.LoginUserInfo;
|
import com.doumee.core.model.PageData;
|
import com.doumee.core.model.PageWrap;
|
import com.doumee.biz.system.SystemDictDataBiz;
|
import com.doumee.core.utils.FtpUtil;
|
import com.doumee.core.utils.Utils;
|
import com.doumee.dao.business.InvoiceRecordMapper;
|
import com.doumee.dao.business.OrdersMapper;
|
import com.doumee.dao.business.model.InvoiceRecord;
|
import com.doumee.dao.business.model.Orders;
|
import com.doumee.dao.dto.ApplyInvoiceDTO;
|
import com.doumee.dao.vo.InvoiceRecordSummaryVO;
|
import com.doumee.service.business.InvoiceRecordService;
|
import com.doumee.service.common.EmailService;
|
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
import org.apache.shiro.SecurityUtils;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.stereotype.Service;
|
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.util.CollectionUtils;
|
|
import java.io.File;
|
import java.io.FileOutputStream;
|
import java.util.*;
|
|
|
/**
|
* 发票申请记录Service实现
|
*
|
* @author rk
|
* @date 2026/05/18
|
*/
|
@Service
|
public class InvoiceRecordServiceImpl implements InvoiceRecordService {
|
|
@Autowired
|
private InvoiceRecordMapper invoiceRecordMapper;
|
|
@Autowired
|
private OrdersMapper ordersMapper;
|
|
@Autowired
|
private EmailService emailService;
|
|
@Autowired
|
private SystemDictDataBiz systemDictDataBiz;
|
|
@Override
|
public Integer create(InvoiceRecord invoiceRecord) {
|
LoginUserInfo loginUserInfo = (LoginUserInfo) SecurityUtils.getSubject().getPrincipal();
|
invoiceRecord.setDeleted(Constants.ZERO);
|
invoiceRecord.setCreateTime(new Date());
|
invoiceRecord.setCreateUser(loginUserInfo.getId());
|
invoiceRecord.setUpdateTime(new Date());
|
invoiceRecord.setUpdateUser(loginUserInfo.getId());
|
invoiceRecordMapper.insert(invoiceRecord);
|
return invoiceRecord.getId();
|
}
|
|
@Override
|
public void deleteById(Integer id) {
|
invoiceRecordMapper.update(new UpdateWrapper<InvoiceRecord>().lambda()
|
.set(InvoiceRecord::getDeleted, Constants.ONE)
|
.eq(InvoiceRecord::getId, id));
|
}
|
|
@Override
|
public void delete(InvoiceRecord invoiceRecord) {
|
UpdateWrapper<InvoiceRecord> deleteWrapper = new UpdateWrapper<>(invoiceRecord);
|
invoiceRecordMapper.delete(deleteWrapper);
|
}
|
|
@Override
|
public void deleteByIdInBatch(List<Integer> ids) {
|
if (CollectionUtils.isEmpty(ids)) {
|
return;
|
}
|
for (Integer id : ids) {
|
this.deleteById(id);
|
}
|
}
|
|
@Override
|
public void updateById(InvoiceRecord invoiceRecord) {
|
LoginUserInfo loginUserInfo = (LoginUserInfo) SecurityUtils.getSubject().getPrincipal();
|
invoiceRecord.setUpdateTime(new Date());
|
invoiceRecord.setUpdateUser(loginUserInfo.getId());
|
invoiceRecordMapper.updateById(invoiceRecord);
|
}
|
|
@Override
|
public void updateByIdInBatch(List<InvoiceRecord> invoiceRecords) {
|
if (CollectionUtils.isEmpty(invoiceRecords)) {
|
return;
|
}
|
for (InvoiceRecord invoiceRecord : invoiceRecords) {
|
this.updateById(invoiceRecord);
|
}
|
}
|
|
@Override
|
public InvoiceRecord findById(Integer id) {
|
InvoiceRecord invoiceRecord = invoiceRecordMapper.selectById(id);
|
if (Objects.isNull(invoiceRecord)) {
|
throw new BusinessException(ResponseStatus.DATA_EMPTY);
|
}
|
return invoiceRecord;
|
}
|
|
@Override
|
public InvoiceRecord findOne(InvoiceRecord invoiceRecord) {
|
QueryWrapper<InvoiceRecord> wrapper = new QueryWrapper<>(invoiceRecord);
|
return invoiceRecordMapper.selectOne(wrapper);
|
}
|
|
@Override
|
public List<InvoiceRecord> findList(InvoiceRecord invoiceRecord) {
|
QueryWrapper<InvoiceRecord> wrapper = new QueryWrapper<>(invoiceRecord);
|
return invoiceRecordMapper.selectList(wrapper);
|
}
|
|
@Override
|
public PageData<InvoiceRecord> findPage(PageWrap<InvoiceRecord> pageWrap) {
|
IPage<InvoiceRecord> page = new Page<>(pageWrap.getPage(), pageWrap.getCapacity());
|
QueryWrapper<InvoiceRecord> qw = buildAdminQueryWrapper(pageWrap.getModel());
|
qw.lambda().orderByDesc(InvoiceRecord::getCreateTime);
|
return PageData.from(invoiceRecordMapper.selectPage(page, qw));
|
}
|
|
@Override
|
public long count(InvoiceRecord invoiceRecord) {
|
QueryWrapper<InvoiceRecord> wrapper = new QueryWrapper<>(invoiceRecord);
|
return invoiceRecordMapper.selectCount(wrapper);
|
}
|
|
@Override
|
@Transactional(rollbackFor = {Exception.class, BusinessException.class})
|
public void applyInvoice(ApplyInvoiceDTO dto, Integer memberId) {
|
// 查询订单
|
Orders order = ordersMapper.selectById(dto.getOrderId());
|
if (order == null || Constants.equalsInteger(order.getDeleted(), Constants.ONE)) {
|
throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "订单不存在");
|
}
|
if (!Constants.equalsInteger(order.getMemberId(), memberId)) {
|
throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "无权操作该订单");
|
}
|
|
// 校验发票状态:只有1=可申请、99=开具失败可以申请
|
if (order.getInvoiceStatus() == null
|
|| (!Constants.equalsInteger(order.getInvoiceStatus(), Constants.ONE)
|
&& !Constants.equalsInteger(order.getInvoiceStatus(), 99))) {
|
throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "当前订单不支持开票申请");
|
}
|
|
// 个人/事业单位只能开具电子普通发票
|
if (Constants.equalsInteger(dto.getOrgType(), Constants.ZERO)
|
&& !Constants.equalsInteger(dto.getInvoiceType(), Constants.ZERO)) {
|
throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "个人/事业单位只能开具电子普通发票");
|
}
|
|
// 企业类型:税号必填
|
if (Constants.equalsInteger(dto.getOrgType(), Constants.ONE)
|
&& StringUtils.isBlank(dto.getTaxId())) {
|
throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "企业类型税号不能为空");
|
}
|
|
// 电子专用发票:开户银行、银行账号、企业地址、企业电话必填
|
if (Constants.equalsInteger(dto.getInvoiceType(), Constants.ONE)) {
|
if (StringUtils.isAnyBlank(dto.getBankName(), dto.getBankAccount(),
|
dto.getCompanyAddr(), dto.getCompanyPhone())) {
|
throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "电子专用发票需填写开户银行、银行账号、企业地址、企业电话");
|
}
|
}
|
|
// 计算开票金额
|
long payAmount = order.getPayAmount() != null ? order.getPayAmount() : 0L;
|
long refundAmount = order.getRefundAmount() != null ? order.getRefundAmount() : 0L;
|
long invoiceAmount = payAmount - refundAmount;
|
|
// 创建发票申请记录
|
InvoiceRecord record = new InvoiceRecord();
|
record.setOrderId(order.getId());
|
record.setOrderNo(order.getCode());
|
record.setMemberId(memberId);
|
record.setOrgType(dto.getOrgType());
|
record.setInvoiceType(dto.getInvoiceType());
|
record.setName(dto.getName());
|
record.setTaxId(dto.getTaxId());
|
record.setBankName(dto.getBankName());
|
record.setBankAccount(dto.getBankAccount());
|
record.setCompanyAddr(dto.getCompanyAddr());
|
record.setCompanyPhone(dto.getCompanyPhone());
|
record.setInvoiceAmount(invoiceAmount);
|
record.setStatus(Constants.ZERO); // 0=申请中
|
record.setDeleted(Constants.ZERO);
|
record.setCreateTime(new Date());
|
invoiceRecordMapper.insert(record);
|
|
// 更新订单发票状态为申请中
|
order.setInvoiceStatus(Constants.TWO);
|
order.setUpdateTime(new Date());
|
ordersMapper.updateById(order);
|
}
|
|
@Override
|
public PageData<InvoiceRecord> findMemberInvoicePage(PageWrap<InvoiceRecord> pageWrap, Integer memberId) {
|
IPage<InvoiceRecord> page = new Page<>(pageWrap.getPage(), pageWrap.getCapacity());
|
QueryWrapper<InvoiceRecord> qw = new QueryWrapper<>();
|
qw.lambda()
|
.eq(InvoiceRecord::getDeleted, Constants.ZERO)
|
.eq(InvoiceRecord::getMemberId, memberId)
|
// .eq(InvoiceRecord::getStatus, Constants.ONE) // 只查已开具成功
|
.orderByDesc(InvoiceRecord::getCreateTime);
|
return PageData.from(invoiceRecordMapper.selectPage(page, qw));
|
}
|
|
@Override
|
public void sendInvoiceEmail(Integer memberId, Integer invoiceRecordId, String email) {
|
InvoiceRecord record = invoiceRecordMapper.selectById(invoiceRecordId);
|
if (record == null || Constants.equalsInteger(record.getDeleted(), Constants.ONE)) {
|
throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "发票记录不存在");
|
}
|
if (!Constants.equalsInteger(record.getMemberId(), memberId)) {
|
throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "无权操作该发票记录");
|
}
|
if (!Constants.equalsInteger(record.getStatus(), Constants.ONE)) {
|
throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "发票尚未开具成功");
|
}
|
if (StringUtils.isBlank(record.getFileAddr())) {
|
throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "发票文件不存在");
|
}
|
|
// 拼接文件完整URL
|
String fullUrl = record.getFileAddr();
|
if (!fullUrl.startsWith("http")) {
|
try {
|
String prefix = systemDictDataBiz.queryByCode(Constants.OSS, Constants.RESOURCE_PATH).getCode();
|
fullUrl = prefix + fullUrl;
|
} catch (Exception e) {
|
throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "文件地址配置缺失");
|
}
|
}
|
|
// 下载发票文件
|
byte[] fileBytes;
|
try {
|
fileBytes = new FtpUtil().getOnlineInputsteam(fullUrl);
|
} catch (Exception e) {
|
throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "发票文件下载失败");
|
}
|
if (fileBytes == null || fileBytes.length == 0) {
|
throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "发票文件为空");
|
}
|
|
// 写入临时文件
|
File tempFile = null;
|
try {
|
tempFile = File.createTempFile("invoice_", ".pdf");
|
try (FileOutputStream fos = new FileOutputStream(tempFile)) {
|
fos.write(fileBytes);
|
}
|
|
// 构建附件列表
|
String fileName = "发票_" + record.getOrderNo() + ".pdf";
|
Map<String, Object> attachment = new HashMap<>();
|
attachment.put("name", fileName);
|
attachment.put("file", tempFile);
|
|
boolean success = emailService.sendWithAttachment(email, "您的电子发票", "您好,您的发票详见附件,请查收。", Collections.singletonList(attachment));
|
if (!success) {
|
throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "邮件发送失败");
|
}
|
} catch (BusinessException e) {
|
throw e;
|
} catch (Exception e) {
|
throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "邮件发送异常");
|
} finally {
|
if (tempFile != null && tempFile.exists()) {
|
tempFile.delete();
|
}
|
}
|
}
|
|
/**
|
* 构建 admin 端发票查询条件(findPage 和 summary 共用)
|
*/
|
private QueryWrapper<InvoiceRecord> buildAdminQueryWrapper(InvoiceRecord model) {
|
Utils.MP.blankToNull(model);
|
model.setDeleted(Constants.ZERO);
|
QueryWrapper<InvoiceRecord> qw = new QueryWrapper<>();
|
qw.lambda().eq(InvoiceRecord::getDeleted, Constants.ZERO);
|
if (model.getInvoiceNo() != null && StringUtils.isNotBlank(model.getInvoiceNo())) {
|
qw.lambda().like(InvoiceRecord::getInvoiceNo, model.getInvoiceNo());
|
}
|
if (model.getInvoiceType() != null) {
|
qw.lambda().eq(InvoiceRecord::getInvoiceType, model.getInvoiceType());
|
}
|
if (model.getMemberId() != null) {
|
qw.lambda().eq(InvoiceRecord::getMemberId, model.getMemberId());
|
}
|
if (model.getOrgType() != null) {
|
qw.lambda().eq(InvoiceRecord::getOrgType, model.getOrgType());
|
}
|
if (model.getName() != null && StringUtils.isNotBlank(model.getName())) {
|
qw.lambda().like(InvoiceRecord::getName, model.getName());
|
}
|
if (model.getStatus() != null) {
|
qw.lambda().eq(InvoiceRecord::getStatus, model.getStatus());
|
}
|
if (model.getStartDate() != null) {
|
qw.lambda().ge(InvoiceRecord::getCreateTime, model.getStartDate());
|
}
|
if (model.getEndDate() != null) {
|
qw.lambda().le(InvoiceRecord::getCreateTime, Utils.Date.getEnd(model.getEndDate()));
|
}
|
return qw;
|
}
|
|
@Override
|
public InvoiceRecordSummaryVO findPageSummary(PageWrap<InvoiceRecord> pageWrap) {
|
QueryWrapper<InvoiceRecord> baseQw = buildAdminQueryWrapper(pageWrap.getModel());
|
|
// 开票总额:符合条件的所有记录
|
QueryWrapper<InvoiceRecord> totalQw = baseQw.clone();
|
totalQw.select("IFNULL(SUM(INVOICE_AMOUNT), 0) as invoiceAmount");
|
Map<String, Object> totalResult = invoiceRecordMapper.selectMaps(totalQw).stream().findFirst().orElse(null);
|
long totalAmount = totalResult != null && totalResult.get("invoiceAmount") != null
|
? Long.parseLong(totalResult.get("invoiceAmount").toString()) : 0L;
|
|
// 已开票总额:status=1
|
QueryWrapper<InvoiceRecord> issuedQw = baseQw.clone();
|
issuedQw.eq("STATUS", Constants.ONE);
|
issuedQw.select("IFNULL(SUM(INVOICE_AMOUNT), 0) as invoiceAmount");
|
Map<String, Object> issuedResult = invoiceRecordMapper.selectMaps(issuedQw).stream().findFirst().orElse(null);
|
long issuedAmount = issuedResult != null && issuedResult.get("invoiceAmount") != null
|
? Long.parseLong(issuedResult.get("invoiceAmount").toString()) : 0L;
|
|
InvoiceRecordSummaryVO vo = new InvoiceRecordSummaryVO();
|
vo.setTotalAmount(totalAmount);
|
vo.setIssuedAmount(issuedAmount);
|
return vo;
|
}
|
}
|