本文目录导读:

我来为您设计一个完整的Java票据(发票)管理系统案例,包含核心功能模块和代码实现。
票据管理系统架构设计
项目结构
ticket-system/
├── src/main/java/com/example/ticket/
│ ├── model/ # 实体类
│ ├── dao/ # 数据访问层
│ ├── service/ # 业务逻辑层
│ ├── controller/ # 控制层
│ └── util/ # 工具类
└── src/main/resources/ # 配置文件
核心实体类设计
1 票据实体类(Ticket)
package com.example.ticket.model;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
/**
* 票据实体类
*/
public class Ticket {
// 票据ID
private Long id;
// 票据编号
private String ticketNo;
// 票据类型
private TicketType type;
// 开票方
private String issuer;
// 收票方
private String payer;
// 票据金额
private BigDecimal amount;
// 税率
private BigDecimal taxRate;
// 税额
private BigDecimal taxAmount;
// 价税合计
private BigDecimal totalAmount;
// 开票日期
private LocalDateTime issueDate;
// 有效期
private LocalDateTime validDate;
// 状态
private TicketStatus status;
// 备注
private String remark;
// 创建时间
private LocalDateTime createTime;
// 更新时间
private LocalDateTime updateTime;
// 票据明细
private List<TicketItem> items;
// 构造函数
public Ticket() {
this.createTime = LocalDateTime.now();
this.updateTime = LocalDateTime.now();
this.status = TicketStatus.PENDING;
}
// Getter和Setter方法
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTicketNo() {
return ticketNo;
}
public void setTicketNo(String ticketNo) {
this.ticketNo = ticketNo;
}
public TicketType getType() {
return type;
}
public void setType(TicketType type) {
this.type = type;
}
public String getIssuer() {
return issuer;
}
public void setIssuer(String issuer) {
this.issuer = issuer;
}
public String getPayer() {
return payer;
}
public void setPayer(String payer) {
this.payer = payer;
}
public BigDecimal getAmount() {
return amount;
}
public void setAmount(BigDecimal amount) {
this.amount = amount;
calculateTax();
}
public BigDecimal getTaxRate() {
return taxRate;
}
public void setTaxRate(BigDecimal taxRate) {
this.taxRate = taxRate;
calculateTax();
}
public BigDecimal getTaxAmount() {
return taxAmount;
}
public void setTaxAmount(BigDecimal taxAmount) {
this.taxAmount = taxAmount;
}
public BigDecimal getTotalAmount() {
return totalAmount;
}
public void setTotalAmount(BigDecimal totalAmount) {
this.totalAmount = totalAmount;
}
public LocalDateTime getIssueDate() {
return issueDate;
}
public void setIssueDate(LocalDateTime issueDate) {
this.issueDate = issueDate;
}
public LocalDateTime getValidDate() {
return validDate;
}
public void setValidDate(LocalDateTime validDate) {
this.validDate = validDate;
}
public TicketStatus getStatus() {
return status;
}
public void setStatus(TicketStatus status) {
this.status = status;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
public LocalDateTime getCreateTime() {
return createTime;
}
public void setCreateTime(LocalDateTime createTime) {
this.createTime = createTime;
}
public LocalDateTime getUpdateTime() {
return updateTime;
}
public void setUpdateTime(LocalDateTime updateTime) {
this.updateTime = updateTime;
}
public List<TicketItem> getItems() {
return items;
}
public void setItems(List<TicketItem> items) {
this.items = items;
}
// 计算税额和总金额
private void calculateTax() {
if (amount != null && taxRate != null) {
this.taxAmount = amount.multiply(taxRate);
this.totalAmount = amount.add(taxAmount);
}
}
}
2 票据明细类(TicketItem)
package com.example.ticket.model;
import java.math.BigDecimal;
/**
* 票据明细实体类
*/
public class TicketItem {
// 明细ID
private Long id;
// 票据ID
private Long ticketId;
// 项目名称
private String itemName;
// 规格型号
private String specification;
// 单位
private String unit;
// 数量
private Integer quantity;
// 单价
private BigDecimal price;
// 金额
private BigDecimal amount;
// 税率
private BigDecimal taxRate;
// 税额
private BigDecimal taxAmount;
// 构造函数
public TicketItem() {
}
public TicketItem(String itemName, Integer quantity, BigDecimal price, BigDecimal taxRate) {
this.itemName = itemName;
this.quantity = quantity;
this.price = price;
this.taxRate = taxRate;
calculateAmount();
}
// 计算金额和税额
private void calculateAmount() {
if (quantity != null && price != null) {
this.amount = price.multiply(BigDecimal.valueOf(quantity));
if (taxRate != null) {
this.taxAmount = amount.multiply(taxRate);
}
}
}
// Getter和Setter方法
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getTicketId() {
return ticketId;
}
public void setTicketId(Long ticketId) {
this.ticketId = ticketId;
}
public String getItemName() {
return itemName;
}
public void setItemName(String itemName) {
this.itemName = itemName;
}
public String getSpecification() {
return specification;
}
public void setSpecification(String specification) {
this.specification = specification;
}
public String getUnit() {
return unit;
}
public void setUnit(String unit) {
this.unit = unit;
}
public Integer getQuantity() {
return quantity;
}
public void setQuantity(Integer quantity) {
this.quantity = quantity;
calculateAmount();
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
calculateAmount();
}
public BigDecimal getAmount() {
return amount;
}
public void setAmount(BigDecimal amount) {
this.amount = amount;
}
public BigDecimal getTaxRate() {
return taxRate;
}
public void setTaxRate(BigDecimal taxRate) {
this.taxRate = taxRate;
calculateAmount();
}
public BigDecimal getTaxAmount() {
return taxAmount;
}
public void setTaxAmount(BigDecimal taxAmount) {
this.taxAmount = taxAmount;
}
}
3 枚举类型定义
package com.example.ticket.model;
/**
* 票据类型枚举
*/
public enum TicketType {
INVOICE("发票"),
RECEIPT("收据"),
VOUCHER("凭证"),
TICKET("车票"),
CHECK("支票");
private String description;
TicketType(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
}
/**
* 票据状态枚举
*/
public enum TicketStatus {
PENDING("待审核"),
APPROVED("已通过"),
REJECTED("已拒绝"),
CANCELLED("已作废"),
EXPIRED("已过期");
private String description;
TicketStatus(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
}
数据访问层(DAO)
package com.example.ticket.dao;
import com.example.ticket.model.Ticket;
import com.example.ticket.model.TicketStatus;
import com.example.ticket.model.TicketType;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
/**
* 票据数据访问层(使用内存存储模拟数据库)
*/
public class TicketDao {
// 使用ConcurrentHashMap模拟数据库存储
private static final Map<Long, Ticket> ticketStore = new ConcurrentHashMap<>();
private static long idGenerator = 1;
// 保存票据
public Ticket save(Ticket ticket) {
if (ticket.getId() == null) {
ticket.setId(idGenerator++);
}
ticket.setUpdateTime(java.time.LocalDateTime.now());
ticketStore.put(ticket.getId(), ticket);
return ticket;
}
// 根据ID查询票据
public Ticket findById(Long id) {
return ticketStore.get(id);
}
// 查询所有票据
public List<Ticket> findAll() {
return new ArrayList<>(ticketStore.values());
}
// 根据状态查询
public List<Ticket> findByStatus(TicketStatus status) {
return ticketStore.values().stream()
.filter(ticket -> ticket.getStatus() == status)
.collect(Collectors.toList());
}
// 根据类型查询
public List<Ticket> findByType(TicketType type) {
return ticketStore.values().stream()
.filter(ticket -> ticket.getType() == type)
.collect(Collectors.toList());
}
// 根据票据编号查询
public Ticket findByTicketNo(String ticketNo) {
return ticketStore.values().stream()
.filter(ticket -> ticket.getTicketNo().equals(ticketNo))
.findFirst()
.orElse(null);
}
// 删除票据
public boolean delete(Long id) {
return ticketStore.remove(id) != null;
}
// 更新票据
public Ticket update(Ticket ticket) {
if (ticketStore.containsKey(ticket.getId())) {
ticket.setUpdateTime(java.time.LocalDateTime.now());
ticketStore.put(ticket.getId(), ticket);
return ticket;
}
return null;
}
// 条件查询
public List<Ticket> search(TicketQuery query) {
return ticketStore.values().stream()
.filter(ticket -> {
if (query.getType() != null && ticket.getType() != query.getType()) {
return false;
}
if (query.getStatus() != null && ticket.getStatus() != query.getStatus()) {
return false;
}
if (query.getIssuer() != null && !ticket.getIssuer().contains(query.getIssuer())) {
return false;
}
if (query.getStartDate() != null && ticket.getIssueDate().isBefore(query.getStartDate())) {
return false;
}
if (query.getEndDate() != null && ticket.getIssueDate().isAfter(query.getEndDate())) {
return false;
}
return true;
})
.collect(Collectors.toList());
}
// 统计数量
public long count() {
return ticketStore.size();
}
// 统计金额
public java.math.BigDecimal sumAmount() {
return ticketStore.values().stream()
.map(Ticket::getTotalAmount)
.reduce(java.math.BigDecimal.ZERO, java.math.BigDecimal::add);
}
}
/**
* 票据查询条件类
*/
class TicketQuery {
private TicketType type;
private TicketStatus status;
private String issuer;
private java.time.LocalDateTime startDate;
private java.time.LocalDateTime endDate;
// Getter和Setter方法
public TicketType getType() {
return type;
}
public void setType(TicketType type) {
this.type = type;
}
public TicketStatus getStatus() {
return status;
}
public void setStatus(TicketStatus status) {
this.status = status;
}
public String getIssuer() {
return issuer;
}
public void setIssuer(String issuer) {
this.issuer = issuer;
}
public java.time.LocalDateTime getStartDate() {
return startDate;
}
public void setStartDate(java.time.LocalDateTime startDate) {
this.startDate = startDate;
}
public java.time.LocalDateTime getEndDate() {
return endDate;
}
public void setEndDate(java.time.LocalDateTime endDate) {
this.endDate = endDate;
}
}
业务逻辑层(Service)
package com.example.ticket.service;
import com.example.ticket.dao.TicketDao;
import com.example.ticket.model.Ticket;
import com.example.ticket.model.TicketStatus;
import com.example.ticket.model.TicketType;
import com.example.ticket.util.TicketNumberGenerator;
import com.example.ticket.util.ValidationUtil;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
/**
* 票据业务逻辑层
*/
public class TicketService {
private final TicketDao ticketDao;
private final TicketNumberGenerator numberGenerator;
public TicketService() {
this.ticketDao = new TicketDao();
this.numberGenerator = new TicketNumberGenerator();
}
/**
* 创建票据
*/
public Ticket createTicket(Ticket ticket) {
// 验证票据数据
ValidationUtil.validateTicket(ticket);
// 生成票据编号
ticket.setTicketNo(numberGenerator.generateTicketNo(ticket.getType()));
// 设置默认状态
if (ticket.getStatus() == null) {
ticket.setStatus(TicketStatus.PENDING);
}
// 设置开票日期
if (ticket.getIssueDate() == null) {
ticket.setIssueDate(LocalDateTime.now());
}
// 设置有效期(默认一年)
if (ticket.getValidDate() == null) {
ticket.setValidDate(LocalDateTime.now().plusYears(1));
}
return ticketDao.save(ticket);
}
/**
* 更新票据
*/
public Ticket updateTicket(Ticket ticket) {
ValidationUtil.validateTicket(ticket);
Ticket existingTicket = ticketDao.findById(ticket.getId());
if (existingTicket == null) {
throw new RuntimeException("票据不存在");
}
// 检查状态
if (existingTicket.getStatus() == TicketStatus.CANCELLED) {
throw new RuntimeException("已作废的票据不能修改");
}
return ticketDao.update(ticket);
}
/**
* 获取所有票据
*/
public List<Ticket> getAllTickets() {
return ticketDao.findAll();
}
/**
* 根据ID获取票据
*/
public Ticket getTicketById(Long id) {
Ticket ticket = ticketDao.findById(id);
if (ticket == null) {
throw new RuntimeException("票据不存在");
}
return ticket;
}
/**
* 审核通过
*/
public boolean approveTicket(Long id) {
Ticket ticket = ticketDao.findById(id);
if (ticket == null) {
throw new RuntimeException("票据不存在");
}
if (ticket.getStatus() == TicketStatus.CANCELLED) {
throw new RuntimeException("已作废的票据不能审核");
}
// 检查是否过期
if (ticket.getValidDate().isBefore(LocalDateTime.now())) {
ticket.setStatus(TicketStatus.EXPIRED);
ticketDao.update(ticket);
throw new RuntimeException("票据已过期");
}
ticket.setStatus(TicketStatus.APPROVED);
ticketDao.update(ticket);
return true;
}
/**
* 拒绝票据
*/
public boolean rejectTicket(Long id, String reason) {
Ticket ticket = ticketDao.findById(id);
if (ticket == null) {
throw new RuntimeException("票据不存在");
}
ticket.setStatus(TicketStatus.REJECTED);
if (reason != null) {
ticket.setRemark(reason);
}
ticketDao.update(ticket);
return true;
}
/**
* 作废票据
*/
public boolean cancelTicket(Long id) {
Ticket ticket = ticketDao.findById(id);
if (ticket == null) {
throw new RuntimeException("票据不存在");
}
ticket.setStatus(TicketStatus.CANCELLED);
ticketDao.update(ticket);
return true;
}
/**
* 删除票据
*/
public boolean deleteTicket(Long id) {
return ticketDao.delete(id);
}
/**
* 检查票据过期
*/
public void checkExpiration() {
List<Ticket> tickets = ticketDao.findAll();
LocalDateTime now = LocalDateTime.now();
for (Ticket ticket : tickets) {
if (ticket.getStatus() == TicketStatus.PENDING &&
ticket.getValidDate().isBefore(now)) {
ticket.setStatus(TicketStatus.EXPIRED);
ticketDao.update(ticket);
}
}
}
/**
* 获取票据统计信息
*/
public TicketStatistics getStatistics() {
List<Ticket> tickets = ticketDao.findAll();
TicketStatistics stats = new TicketStatistics();
stats.setTotalCount(tickets.size());
stats.setTotalAmount(tickets.stream()
.map(Ticket::getTotalAmount)
.reduce(BigDecimal.ZERO, BigDecimal::add));
// 统计各状态数量
for (TicketStatus status : TicketStatus.values()) {
long count = tickets.stream()
.filter(t -> t.getStatus() == status)
.count();
stats.getStatusCounts().put(status, count);
}
// 统计各类型数量
for (TicketType type : TicketType.values()) {
long count = tickets.stream()
.filter(t -> t.getType() == type)
.count();
stats.getTypeCounts().put(type, count);
}
return stats;
}
}
工具类
package com.example.ticket.util;
import com.example.ticket.model.Ticket;
import com.example.ticket.model.TicketType;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Random;
/**
* 票据编号生成器
*/
public class TicketNumberGenerator {
private static final DateTimeFormatter DATE_FORMAT =
DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
private static final Random RANDOM = new Random();
/**
* 生成票据编号
*/
public String generateTicketNo(TicketType type) {
StringBuilder sb = new StringBuilder();
// 添加类型前缀
String typePrefix = getTypePrefix(type);
sb.append(typePrefix);
// 添加日期时间
sb.append(LocalDateTime.now().format(DATE_FORMAT));
// 添加随机数
sb.append(String.format("%04d", RANDOM.nextInt(10000)));
return sb.toString();
}
/**
* 获取类型前缀
*/
private String getTypePrefix(TicketType type) {
switch (type) {
case INVOICE: return "INV";
case RECEIPT: return "RCT";
case VOUCHER: return "VCH";
case TICKET: return "TKT";
case CHECK: return "CHK";
default: return "TIC";
}
}
}
package com.example.ticket.util;
import com.example.ticket.model.Ticket;
import java.math.BigDecimal;
import java.util.List;
/**
* 数据验证工具类
*/
public class ValidationUtil {
/**
* 验证票据数据
*/
public static void validateTicket(Ticket ticket) {
if (ticket == null) {
throw new IllegalArgumentException("票据不能为空");
}
// 验证必填字段
if (ticket.getType() == null) {
throw new IllegalArgumentException("票据类型不能为空");
}
if (ticket.getIssuer() == null || ticket.getIssuer().trim().isEmpty()) {
throw new IllegalArgumentException("开票方不能为空");
}
if (ticket.getPayer() == null || ticket.getPayer().trim().isEmpty()) {
throw new IllegalArgumentException("收票方不能为空");
}
if (ticket.getAmount() == null || ticket.getAmount().compareTo(BigDecimal.ZERO) <= 0) {
throw new IllegalArgumentException("票据金额必须大于0");
}
if (ticket.getTaxRate() == null ||
ticket.getTaxRate().compareTo(BigDecimal.ZERO) < 0 ||
ticket.getTaxRate().compareTo(new BigDecimal("1")) > 0) {
throw new IllegalArgumentException("税率必须在0到1之间");
}
}
/**
* 验证票据列表
*/
public static void validateTickets(List<Ticket> tickets) {
if (tickets == null || tickets.isEmpty()) {
throw new IllegalArgumentException("票据列表不能为空");
}
for (Ticket ticket : tickets) {
validateTicket(ticket);
}
}
}
主程序测试类
package com.example.ticket;
import com.example.ticket.model.Ticket;
import com.example.ticket.model.TicketItem;
import com.example.ticket.model.TicketType;
import com.example.ticket.service.TicketService;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
/**
* 票据管理系统主程序
*/
public class Main {
public static void main(String[] args) {
TicketService ticketService = new TicketService();
System.out.println("========== 票据管理系统演示 ==========");
// 创建票据
try {
// 创建一张发票
Ticket invoice = new Ticket();
invoice.setType(TicketType.INVOICE);
invoice.setIssuer("科技公司");
invoice.setPayer("客户A");
invoice.setAmount(new BigDecimal("1000.00"));
invoice.setTaxRate(new BigDecimal("0.13"));
// 创建票据明细
List<TicketItem> items = new ArrayList<>();
items.add(new TicketItem("软件服务", 10, new BigDecimal("100"), new BigDecimal("0.13")));
invoice.setItems(items);
Ticket createdInvoice = ticketService.createTicket(invoice);
System.out.println("创建发票成功:" + createdInvoice.getTicketNo());
// 审核通过
ticketService.approveTicket(createdInvoice.getId());
System.out.println("票据审核通过");
// 创建一张收据
Ticket receipt = new Ticket();
receipt.setType(TicketType.RECEIPT);
receipt.setIssuer("咨询公司");
receipt.setPayer("客户B");
receipt.setAmount(new BigDecimal("500.00"));
receipt.setTaxRate(new BigDecimal("0.06"));
Ticket createdReceipt = ticketService.createTicket(receipt);
System.out.println("创建收据成功:" + createdReceipt.getTicketNo());
// 获取所有票据
System.out.println("\n所有票据列表:");
ticketService.getAllTickets().forEach(t -> {
System.out.println("- " + t.getTicketNo() + " | " +
t.getType().getDescription() + " | " +
t.getIssuer() + " | " +
t.getTotalAmount() + "元 | " +
t.getStatus().getDescription());
});
// 获取统计信息
var stats = ticketService.getStatistics();
System.out.println("\n统计分析:");
System.out.println("- 票据总数:" + stats.getTotalCount());
System.out.println("- 总金额:" + stats.getTotalAmount() + "元");
System.out.println("- 各状态统计:" + stats.getStatusCounts());
System.out.println("- 各类型统计:" + stats.getTypeCounts());
} catch (Exception e) {
System.err.println("错误:" + e.getMessage());
}
System.out.println("\n========== 演示结束 ==========");
}
}
统计类
package com.example.ticket.service;
import com.example.ticket.model.TicketStatus;
import com.example.ticket.model.TicketType;
import java.math.BigDecimal;
import java.util.EnumMap;
import java.util.Map;
/**
* 票据统计信息类
*/
public class TicketStatistics {
private long totalCount;
private BigDecimal totalAmount;
private Map<TicketStatus, Long> statusCounts = new EnumMap<>(TicketStatus.class);
private Map<TicketType, Long> typeCounts = new EnumMap<>(TicketType.class);
// Getters and Setters
public long getTotalCount() {
return totalCount;
}
public void setTotalCount(long totalCount) {
this.totalCount = totalCount;
}
public BigDecimal getTotalAmount() {
return totalAmount;
}
public void setTotalAmount(BigDecimal totalAmount) {
this.totalAmount = totalAmount;
}
public Map<TicketStatus, Long> getStatusCounts() {
return statusCounts;
}
public void setStatusCounts(Map<TicketStatus, Long> statusCounts) {
this.statusCounts = statusCounts;
}
public Map<TicketType, Long> getTypeCounts() {
return typeCounts;
}
public void setTypeCounts(Map<TicketType, Long> typeCounts) {
this.typeCounts = typeCounts;
}
}
系统特点
- 完整性:覆盖票据的创建、审核、作废、查询、统计等完整生命周期
- 可扩展性:采用分层架构,便于扩展和维护
- 业务规则:包含税率计算、有效期检查、状态管理等业务规则
- 数据验证:提供完善的数据验证机制
- 统计功能:支持多维度统计分析
使用场景
- 企业财务管理系统
- 电商平台发票管理
- 票据扫描识别系统
- 报销管理系统
这个案例可以作为票据管理系统的核心框架,您可以根据实际需求在此基础上扩展数据库集成、Web界面、报表生成等功能。