javacodeadmin/ruoyi-system/src/main/java/com/ruoyi/system/controllerUtil/InvoiceUtil.java

639 lines
27 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package com.ruoyi.system.ControllerUtil;
import com.github.pagehelper.PageHelper;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.system.domain.*;
import com.ruoyi.system.service.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
/**
* 发票工具类
*
* @author ruoyi
* @date 2025-01-26
*/
public class InvoiceUtil {
private static final Logger logger = LoggerFactory.getLogger(InvoiceUtil.class);
private static final IUsersInvoiceInfoService usersInvoiceInfoService = SpringUtils.getBean(IUsersInvoiceInfoService.class);
private static final IOrderService orderService = SpringUtils.getBean(IOrderService.class);
private static final IGoodsOrderService goodsOrderService = SpringUtils.getBean(IGoodsOrderService.class);
private static final IUserMemberRechargeLogService userMemberRechargeLogService = SpringUtils.getBean(IUserMemberRechargeLogService.class);
private static final IUsersService usersService = SpringUtils.getBean(IUsersService.class);
/**
* 获取用户发票中心数据
*
* @param userId 用户ID
* @param page 页码
* @param limit 每页数量
* @return 发票中心数据
*/
public static Map<String, Object> getUserInvoiceCenter(Long userId, int page, int limit) {
try {
Map<String, Object> result = new HashMap<>();
// 1. 获取待开票订单列表
List<Map<String, Object>> pendingInvoices = getPendingInvoiceOrders(userId, page, limit);
// 2. 获取已开票列表
List<Map<String, Object>> completedInvoices = getCompletedInvoices(userId, page, limit);
// 3. 统计数据
Map<String, Object> statistics = getInvoiceStatistics(userId);
result.put("pendingInvoices", pendingInvoices);
result.put("completedInvoices", completedInvoices);
result.put("statistics", statistics);
return result;
} catch (Exception e) {
logger.error("获取用户发票中心数据失败", e);
return null;
}
}
/**
* 获取待开票订单列表
*
* @param userId 用户ID
* @param page 页码
* @param limit 每页数量
* @return 待开票订单列表
*/
public static List<Map<String, Object>> getPendingInvoiceOrders(Long userId, int page, int limit) {
try {
List<Map<String, Object>> pendingOrders = new ArrayList<>();
// 设置分页
PageHelper.startPage(page, limit);
// 1. 查询服务订单(已完成且未开票)
Order orderQuery = new Order();
orderQuery.setUid(userId);
orderQuery.setStatus(4L); // 已完成状态
List<Order> serviceOrders = orderService.selectOrderList(orderQuery);
for (Order order : serviceOrders) {
// 检查是否已开票
if (!isOrderInvoiced(order.getOrderId())) {
Map<String, Object> orderMap = new HashMap<>();
orderMap.put("orderId", order.getOrderId());
orderMap.put("orderType", "service"); // 服务订单
orderMap.put("orderTypeText", "服务订单");
orderMap.put("amount", order.getTotalPrice());
orderMap.put("title", "服务订单"+order.getTotalPrice()+"" );
orderMap.put("createTime", order.getCreateTime());
orderMap.put("canInvoice", true);
pendingOrders.add(orderMap);
}
}
// 2. 查询商品订单(已完成且未开票)
GoodsOrder goodsOrderQuery = new GoodsOrder();
goodsOrderQuery.setUid(userId);
goodsOrderQuery.setStatus(4L); // 已完成状态
List<GoodsOrder> goodsOrders = goodsOrderService.selectGoodsOrderList(goodsOrderQuery);
for (GoodsOrder goodsOrder : goodsOrders) {
// 检查是否已开票
if (!isOrderInvoiced(goodsOrder.getOrderId())) {
Map<String, Object> orderMap = new HashMap<>();
orderMap.put("orderId", goodsOrder.getOrderId());
orderMap.put("orderType", "goods"); // 商品订单
orderMap.put("orderTypeText", "商品订单");
orderMap.put("amount", goodsOrder.getTotalPrice());
orderMap.put("title", "购买商品"+goodsOrder.getTotalPrice()+"");
orderMap.put("createTime", goodsOrder.getCreateTime());
orderMap.put("canInvoice", true);
pendingOrders.add(orderMap);
}
}
// 3. 查询充值订单(已完成且未开票)
UserMemberRechargeLog rechargeQuery = new UserMemberRechargeLog();
rechargeQuery.setUid(Math.toIntExact(userId));
rechargeQuery.setPaytype(1); // 已完成状态
List<UserMemberRechargeLog> rechargeOrders = userMemberRechargeLogService.selectUserMemberRechargeLogList(rechargeQuery);
for (UserMemberRechargeLog recharge : rechargeOrders) {
// 检查是否已开票
if (!isOrderInvoiced(recharge.getOrderid())) {
Map<String, Object> orderMap = new HashMap<>();
orderMap.put("orderId", recharge.getOrderid());
orderMap.put("orderType", "recharge"); // 充值订单
orderMap.put("orderTypeText", "充值订单");
orderMap.put("amount", recharge.getInmoney());
orderMap.put("title", "会员充值"+recharge.getInmoney()+"");
orderMap.put("createTime", recharge.getCreatedAt());
orderMap.put("canInvoice", true);
pendingOrders.add(orderMap);
}
}
// // 按创建时间倒序排列
// pendingOrders.sort((a, b) -> {
// Date timeA = (Date) a.get("createTime");
// Date timeB = (Date) b.get("createTime");
// return timeB.compareTo(timeA);
// });
return pendingOrders;
} catch (Exception e) {
logger.error("获取待开票订单列表失败", e);
return new ArrayList<>();
}
}
/**
* 获取已开票列表
*
* @param userId 用户ID
* @param page 页码
* @param limit 每页数量
* @return 已开票列表
*/
public static List<Map<String, Object>> getCompletedInvoices(Long userId, int page, int limit) {
try {
// 设置分页
PageHelper.startPage(page, limit);
UsersInvoiceInfo query = new UsersInvoiceInfo();
query.setUid(userId.intValue());
List<UsersInvoiceInfo> invoiceList = usersInvoiceInfoService.selectUsersInvoiceInfoList(query);
List<Map<String, Object>> completedInvoices = new ArrayList<>();
for (UsersInvoiceInfo invoice : invoiceList) {
Map<String, Object> invoiceMap = new HashMap<>();
invoiceMap.put("id", invoice.getId());
invoiceMap.put("orderId", invoice.getOrderid());
invoiceMap.put("invoiceTitle", invoice.getInvoiceTitle());
invoiceMap.put("amount", invoice.getInvoicemoney());
invoiceMap.put("invoiceText", invoice.getInvoicetext());
invoiceMap.put("status", invoice.getStatus());
invoiceMap.put("statusText", getInvoiceStatusText(invoice.getStatus()));
invoiceMap.put("type", invoice.getType());
invoiceMap.put("typeText", getInvoiceTypeText(invoice.getType()));
invoiceMap.put("category", invoice.getCategory());
invoiceMap.put("categoryText", invoice.getCategory() == 0 ? "个人" : "企业");
invoiceMap.put("createTime", invoice.getCreatedAt());
invoiceMap.put("hasFile", StringUtils.isNotEmpty(invoice.getFiledata()));
invoiceMap.put("fileUrl", invoice.getFiledata());
completedInvoices.add(invoiceMap);
}
return completedInvoices;
} catch (Exception e) {
logger.error("获取已开票列表失败", e);
return new ArrayList<>();
}
}
/**
* 获取发票统计数据
*
* @param userId 用户ID
* @return 统计数据
*/
public static Map<String, Object> getInvoiceStatistics(Long userId) {
try {
Map<String, Object> statistics = new HashMap<>();
// 待开票数量
List<Map<String, Object>> pendingOrders = getPendingInvoiceOrders(userId, 1, 1000);
int pendingCount = pendingOrders.size();
// 已开票数量
UsersInvoiceInfo query = new UsersInvoiceInfo();
query.setUid(userId.intValue());
List<UsersInvoiceInfo> invoiceList = usersInvoiceInfoService.selectUsersInvoiceInfoList(query);
int completedCount = invoiceList.size();
// 已开票总金额
BigDecimal totalAmount = invoiceList.stream()
.filter(invoice -> invoice.getInvoicemoney() != null)
.map(UsersInvoiceInfo::getInvoicemoney)
.reduce(BigDecimal.ZERO, BigDecimal::add);
statistics.put("pendingCount", pendingCount);
statistics.put("completedCount", completedCount);
statistics.put("totalAmount", totalAmount);
return statistics;
} catch (Exception e) {
logger.error("获取发票统计数据失败", e);
Map<String, Object> statistics = new HashMap<>();
statistics.put("pendingCount", 0);
statistics.put("completedCount", 0);
statistics.put("totalAmount", BigDecimal.ZERO);
return statistics;
}
}
/**
* 批量开具发票
*
* @param userId 用户ID
* @param orderIds 订单ID数组
* @param invoiceData 发票数据
* @return 开票结果
*/
public static AjaxResult createBatchInvoice(Long userId, List<String> orderIds, Map<String, Object> invoiceData) {
try {
// 1. 参数验证
if (orderIds == null || orderIds.isEmpty()) {
return AppletControllerUtil.appletWarning("请选择要开票的订单");
}
// 2. 验证发票数据
String validateResult = validateInvoiceData(invoiceData);
if (StringUtils.isNotEmpty(validateResult)) {
return AppletControllerUtil.appletWarning(validateResult);
}
List<String> successOrders = new ArrayList<>();
List<String> failedOrders = new ArrayList<>();
BigDecimal totalAmount = BigDecimal.ZERO;
List<String> invoiceTexts = new ArrayList<>();
// 3. 逐个处理订单
for (String orderId : orderIds) {
try {
// 验证订单是否存在且属于当前用户
Map<String, Object> orderInfo = getOrderInfo(orderId, userId);
if (orderInfo == null) {
failedOrders.add(orderId + "(订单不存在或不属于当前用户)");
continue;
}
// 检查是否已开票
if (isOrderInvoiced(orderId)) {
failedOrders.add(orderId + "(已开过发票)");
continue;
}
// 累计金额和开票内容
BigDecimal orderAmount = (BigDecimal) orderInfo.get("amount");
if (orderAmount != null) {
totalAmount = totalAmount.add(orderAmount);
}
invoiceTexts.add((String) orderInfo.get("title"));
successOrders.add(orderId);
} catch (Exception e) {
failedOrders.add(orderId + "(处理异常: " + e.getMessage() + ")");
}
}
// 4. 如果没有可开票的订单
if (successOrders.isEmpty()) {
return AppletControllerUtil.appletWarning("没有可开票的订单:" + String.join(", ", failedOrders));
}
// 5. 创建发票记录
UsersInvoiceInfo invoice = new UsersInvoiceInfo();
invoice.setUid(userId.intValue());
invoice.setOrderid(String.join(",", successOrders)); // 多个订单ID用逗号分隔
invoice.setInvoiceTitle((String) invoiceData.get("invoiceTitle"));
invoice.setType((Integer) invoiceData.get("type"));
invoice.setCategory((Integer) invoiceData.get("category"));
invoice.setInvoicemoney(totalAmount); // 总金额
invoice.setInvoicetext(String.join("", invoiceTexts)); // 开票内容合并
invoice.setStatus(1); // 待开票
invoice.setCreatedAt(new Date());
// 根据发票类别设置不同字段
if ((Integer) invoiceData.get("category") == 0) {
// 个人发票
invoice.setEmail((String) invoiceData.get("email"));
// 清空企业字段
invoice.setTaxNumber(null);
invoice.setBankName(null);
invoice.setBankAccount(null);
invoice.setAddress(null);
invoice.setPhone(null);
invoice.setWechat(null);
} else {
// 企业发票
invoice.setTaxNumber((String) invoiceData.get("taxNumber"));
invoice.setAddress((String) invoiceData.get("address"));
invoice.setPhone((String) invoiceData.get("phone"));
invoice.setBankName((String) invoiceData.get("bankName"));
invoice.setBankAccount((String) invoiceData.get("bankAccount"));
invoice.setEmail((String) invoiceData.get("email"));
invoice.setWechat((String) invoiceData.get("wechat"));
}
// 6. 保存发票信息
int result = usersInvoiceInfoService.insertUsersInvoiceInfo(invoice);
if (result > 0) {
Map<String, Object> resultData = new HashMap<>();
resultData.put("successCount", successOrders.size());
resultData.put("successOrders", successOrders);
resultData.put("totalAmount", totalAmount);
resultData.put("invoiceId", invoice.getId());
if (!failedOrders.isEmpty()) {
resultData.put("failedCount", failedOrders.size());
resultData.put("failedOrders", failedOrders);
resultData.put("message", "发票申请提交成功,成功开票 " + successOrders.size() + " 个订单,失败 " + failedOrders.size() + " 个订单");
return AppletControllerUtil.appletSuccess(resultData);
} else {
resultData.put("message", "发票申请提交成功,共开票 " + successOrders.size() + " 个订单");
return AppletControllerUtil.appletSuccess(resultData);
}
} else {
return AppletControllerUtil.appletWarning("发票申请提交失败");
}
} catch (Exception e) {
logger.error("批量开具发票失败", e);
return AppletControllerUtil.appletError("批量开具发票失败:" + e.getMessage());
}
}
/**
* 开具发票(单个订单,兼容旧接口)
*
* @param userId 用户ID
* @param orderId 订单ID
* @param invoiceData 发票数据
* @return 开票结果
*/
public static AjaxResult createInvoice(Long userId, String orderId, Map<String, Object> invoiceData) {
List<String> orderIds = new ArrayList<>();
orderIds.add(orderId);
return createBatchInvoice(userId, orderIds, invoiceData);
}
/**
* 获取订单信息
*
* @param orderId 订单ID
* @param userId 用户ID
* @return 订单信息
*/
private static Map<String, Object> getOrderInfo(String orderId, Long userId) {
try {
// 1. 查询服务订单
Order serviceOrder = orderService.selectOrderByOrderId(orderId);
if (serviceOrder != null && serviceOrder.getUid().equals(userId)) {
Map<String, Object> orderInfo = new HashMap<>();
orderInfo.put("orderId", serviceOrder.getOrderId());
orderInfo.put("amount", serviceOrder.getTotalPrice());
orderInfo.put("title", serviceOrder.getOrderId()+ "服务订单");
orderInfo.put("type", "service");
return orderInfo;
}
// 2. 查询商品订单
GoodsOrder goodsOrder = goodsOrderService.selectGoodsOrderByorderId(orderId);
if (goodsOrder != null && goodsOrder.getUid().equals(userId)) {
Map<String, Object> orderInfo = new HashMap<>();
orderInfo.put("orderId", goodsOrder.getOrderId());
orderInfo.put("amount", goodsOrder.getTotalPrice());
orderInfo.put("title", "商品订单");
orderInfo.put("type", "goods");
return orderInfo;
}
// 3. 查询充值订单
UserMemberRechargeLog rechargeOrder = userMemberRechargeLogService.selectUserMemberRechargeLogByorderid(orderId);
if (rechargeOrder != null && rechargeOrder.getUid().equals(userId)) {
Map<String, Object> orderInfo = new HashMap<>();
orderInfo.put("orderId", rechargeOrder.getOrderid());
orderInfo.put("amount", rechargeOrder.getInmoney());
orderInfo.put("title", rechargeOrder.getReamk() != null ? rechargeOrder.getReamk() : "会员充值");
orderInfo.put("type", "recharge");
return orderInfo;
}
return null;
} catch (Exception e) {
logger.error("获取订单信息失败", e);
return null;
}
}
/**
* 检查订单是否已开票
*
* @param orderId 订单ID
* @return 是否已开票
*/
private static boolean isOrderInvoiced(String orderId) {
try {
// 查询所有发票记录
UsersInvoiceInfo query = new UsersInvoiceInfo();
List<UsersInvoiceInfo> invoiceList = usersInvoiceInfoService.selectUsersInvoiceInfoList(query);
// 检查订单ID是否在任何发票记录中
for (UsersInvoiceInfo invoice : invoiceList) {
if (invoice.getOrderid() != null) {
// 支持单个订单ID或多个订单ID逗号分隔
String[] orderIds = invoice.getOrderid().split(",");
for (String id : orderIds) {
if (id.trim().equals(orderId)) {
return true;
}
}
}
}
return false;
} catch (Exception e) {
logger.error("检查订单开票状态失败", e);
return false;
}
}
/**
* 验证发票数据
*
* @param invoiceData 发票数据
* @return 验证结果,为空表示验证通过
*/
private static String validateInvoiceData(Map<String, Object> invoiceData) {
// 基础字段验证
if (invoiceData.get("invoiceTitle") == null || StringUtils.isEmpty(invoiceData.get("invoiceTitle").toString())) {
return "发票抬头不能为空";
}
if (invoiceData.get("type") == null) {
return "发票类型不能为空";
}
if (invoiceData.get("category") == null) {
return "发票类别不能为空";
}
Integer category = (Integer) invoiceData.get("category");
String invoiceTitle = invoiceData.get("invoiceTitle").toString();
// 个人发票验证
if (category == 0) {
if (invoiceTitle.length() > 50) {
return "个人发票抬头长度不能超过50个字符";
}
// 检查是否填写了企业字段
// if (StringUtils.isNotEmpty((String) invoiceData.get("taxNumber"))) {
// return "个人发票不能填写纳税人识别号";
// }
// if (StringUtils.isNotEmpty((String) invoiceData.get("address"))) {
// return "个人发票不能填写单位地址";
// }
// if (StringUtils.isNotEmpty((String) invoiceData.get("phone"))) {
// return "个人发票不能填写联系电话";
// }
// if (StringUtils.isNotEmpty((String) invoiceData.get("wechat"))) {
// return "个人发票不能填写微信号";
// }
if (StringUtils.isEmpty((String) invoiceData.get("email"))) {
return "邮箱不能为空";
}
if (StringUtils.isEmpty((String) invoiceData.get("wechat"))) {
return "微信号不能为空";
}
}
// 企业发票验证
else if (category == 1) {
if (invoiceTitle.length() > 100) {
return "企业发票抬头长度不能超过100个字符";
}
// 必填字段验证
if (StringUtils.isEmpty((String) invoiceData.get("taxNumber"))) {
return "企业发票的纳税人识别号不能为空";
}
if (StringUtils.isEmpty((String) invoiceData.get("address"))) {
return "企业发票的单位地址不能为空";
}
if (StringUtils.isEmpty((String) invoiceData.get("phone"))) {
return "企业发票的联系电话不能为空";
}
if (StringUtils.isEmpty((String) invoiceData.get("bankName"))) {
return "企业发票的开户银行不能为空";
}
if (StringUtils.isEmpty((String) invoiceData.get("bankAccount"))) {
return "企业发票的银行账号不能为空";
}
// 格式验证
String taxNumber = (String) invoiceData.get("taxNumber");
if (!taxNumber.matches("^[A-Z0-9]{15,20}$")) {
return "税号格式不正确";
}
String phone = (String) invoiceData.get("phone");
if (!phone.matches("^1[3-9]\\d{9}$|^0\\d{2,3}-?\\d{7,8}$|^400-?\\d{3}-?\\d{4}$")) {
return "联系电话格式不正确";
}
if (StringUtils.isEmpty((String) invoiceData.get("email"))) {
return "邮箱不能为空";
}
if (StringUtils.isEmpty((String) invoiceData.get("wechat"))) {
return "微信号不能为空";
}
}
return null; // 验证通过
}
/**
* 获取发票状态文本
*
* @param status 状态值
* @return 状态文本
*/
private static String getInvoiceStatusText(Integer status) {
if (status == null) return "未知";
switch (status) {
case 1: return "待开票";
case 2: return "已开票";
case 3: return "已完成";
default: return "未知";
}
}
/**
* 获取发票类型文本
*
* @param type 类型值
* @return 类型文本
*/
private static String getInvoiceTypeText(Integer type) {
if (type == null) return "未知";
switch (type) {
case 1: return "增值税专用发票";
case 2: return "增值税普通发票";
case 3: return "电子发票";
default: return "未知";
}
}
/**
* 获取用户发票信息列表(用于选择已保存的发票信息)
*
* @param userId 用户ID
* @return 发票信息列表
*/
public static List<Map<String, Object>> getUserInvoiceInfoList(Long userId) {
try {
UsersInvoiceInfo query = new UsersInvoiceInfo();
query.setUid(userId.intValue());
List<UsersInvoiceInfo> invoiceList = usersInvoiceInfoService.selectUsersInvoiceInfoList(query);
// 去重,只保留不同的发票抬头
Map<String, UsersInvoiceInfo> uniqueInvoices = new HashMap<>();
for (UsersInvoiceInfo invoice : invoiceList) {
String key = invoice.getCategory() + "_" + invoice.getInvoiceTitle();
if (!uniqueInvoices.containsKey(key)) {
uniqueInvoices.put(key, invoice);
}
}
List<Map<String, Object>> result = new ArrayList<>();
for (UsersInvoiceInfo invoice : uniqueInvoices.values()) {
Map<String, Object> invoiceMap = new HashMap<>();
invoiceMap.put("id", invoice.getId());
invoiceMap.put("invoiceTitle", invoice.getInvoiceTitle());
invoiceMap.put("category", invoice.getCategory());
invoiceMap.put("categoryText", invoice.getCategory() == 0 ? "个人" : "企业");
invoiceMap.put("taxNumber", invoice.getTaxNumber());
invoiceMap.put("address", invoice.getAddress());
invoiceMap.put("phone", invoice.getPhone());
invoiceMap.put("bankName", invoice.getBankName());
invoiceMap.put("bankAccount", invoice.getBankAccount());
invoiceMap.put("email", invoice.getEmail());
invoiceMap.put("wechat", invoice.getWechat());
result.add(invoiceMap);
}
return result;
} catch (Exception e) {
logger.error("获取用户发票信息列表失败", e);
return new ArrayList<>();
}
}
}