本文将指导你从零开始构建一个功能完整的档案管理系统收费明细模块。该模块将实现费用项目配置、账单生成、支付记录与统计查询等核心功能,采用前后端分离架构,确保代码清晰、易于维护和扩展。
在开始编码前,请确保你的开发环境已就绪。
你需要安装以下软件:
使用Spring Initializr快速创建项目:
访问 https://start.spring.io/
选择项目类型:Maven Project
语言:Java
Spring Boot版本:3.1.5
Group:com.example
Artifact:archive-fee-system
依赖项:Spring Web, Spring Data JPA, MySQL Driver, Lombok
点击“GENERATE”下载项目压缩包并解压。
你需要安装Node.js和包管理工具。
npm install -g @vue/cli
创建Vue项目:
vue create archive-fee-frontend
选择Vue 3预设,并手动添加Router和Axios。
在MySQL中创建数据库和表结构。以下是核心表SQL脚本。
CREATE DATABASE IF NOT EXISTS `archive_fee_db` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE `archive_fee_db`;
-- 费用项目配置表
CREATE TABLE `fee_item` (
`id` bigint NOT NULL AUTO_INCREMENT,
`item_code` varchar(50) NOT NULL COMMENT '费用项目编码',
`item_name` varchar(100) NOT NULL COMMENT '费用项目名称',
`unit_price` decimal(10,2) NOT NULL COMMENT '单价',
`currency` varchar(10) DEFAULT 'CNY' COMMENT '币种',
`is_active` tinyint(1) DEFAULT '1' COMMENT '是否启用',
`description` varchar(500) DEFAULT NULL COMMENT '描述',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_item_code` (`item_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='费用项目表';
-- 档案收费账单主表
CREATE TABLE `fee_bill` (
`id` bigint NOT NULL AUTO_INCREMENT,
`bill_no` varchar(64) NOT NULL COMMENT '账单编号',
`archive_id` varchar(100) NOT NULL COMMENT '关联档案ID',
`total_amount` decimal(10,2) NOT NULL COMMENT '账单总金额',
`status` tinyint NOT NULL COMMENT '状态:0-待支付,1-已支付,2-已取消',
`payer_name` varchar(100) DEFAULT NULL COMMENT '付款人姓名',
`payer_contact` varchar(100) DEFAULT NULL COMMENT '付款人联系方式',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
`pay_time` datetime DEFAULT NULL COMMENT '支付时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_bill_no` (`bill_no`),
KEY `idx_archive_id` (`archive_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='收费账单主表';
-- 账单明细表
CREATE TABLE `fee_bill_detail` (
`id` bigint NOT NULL AUTO_INCREMENT,
`bill_id` bigint NOT NULL COMMENT '关联账单ID',
`fee_item_id` bigint NOT NULL COMMENT '费用项目ID',
`quantity` int NOT NULL DEFAULT '1' COMMENT '数量',
`unit_price` decimal(10,2) NOT NULL COMMENT '单价',
`subtotal` decimal(10,2) NOT NULL COMMENT '小计',
`remark` varchar(200) DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`id`),
KEY `idx_bill_id` (`bill_id`),
CONSTRAINT `fk_detail_bill` FOREIGN KEY (`bill_id`) REFERENCES `fee_bill` (`id`) ON DELETE CASCADE,
CONSTRAINT `fk_detail_item` FOREIGN KEY (`fee_item_id`) REFERENCES `fee_item` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='账单明细表';
-- 支付记录表
CREATE TABLE `payment_record` (
`id` bigint NOT NULL AUTO_INCREMENT,
`bill_id` bigint NOT NULL COMMENT '关联账单ID',
`payment_no` varchar(64) NOT NULL COMMENT '支付平台流水号',
`payment_method` varchar(20) NOT NULL COMMENT '支付方式:WECHAT, ALIPAY, CASH等',
`paid_amount` decimal(10,2) NOT NULL COMMENT '实付金额',
`payment_status` varchar(20) NOT NULL COMMENT '支付状态:SUCCESS, FAILED, PENDING',
`payment_time` datetime DEFAULT NULL COMMENT '支付完成时间',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_payment_no` (`payment_no`),
KEY `idx_bill_id` (`bill_id`),
CONSTRAINT `fk_payment_bill` FOREIGN KEY (`bill_id`) REFERENCES `fee_bill` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='支付记录表';
打开解压后的Spring Boot项目,在application.yml中配置数据库连接。

spring:
datasource:
url: jdbc:mysql://localhost:3306/archive_fee_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
username: root
password: your_password
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
hibernate:
ddl-auto: update
show-sql: true
properties:
hibernate:
dialect: org.hibernate.dialect.MySQL8Dialect
format_sql: true
在src/main/java/com/example/archivefeesystem/entity目录下创建实体类。以FeeBill为例:
package com.example.archivefeesystem.entity;
import jakarta.persistence.;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Entity
@Table(name = "fee_bill")
@Data
public class FeeBill {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "bill_no", nullable = false, unique = true, length = 64)
private String billNo;
@Column(name = "archive_id", nullable = false, length = 100)
private String archiveId;
@Column(name = "total_amount", nullable = false, precision = 10, scale = 2)
private BigDecimal totalAmount;
@Column(name = "status", nullable = false)
private Integer status; // 0-待支付,1-已支付,2-已取消
@Column(name = "payer_name", length = 100)
private String payerName;
@Column(name = "payer_contact", length = 100)
private String payerContact;
@Column(name = "create_time")
private LocalDateTime createTime;
@Column(name = "pay_time")
private LocalDateTime payTime;
}
按照同样方式创建FeeItem, FeeBillDetail, PaymentRecord实体类。
在src/main/java/com/example/archivefeesystem/repository目录下创建JPA Repository接口。
package com.example.archivefeesystem.repository;
import com.example.archivefeesystem.entity.FeeBill;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import java.util.Optional;
public interface FeeBillRepository extends JpaRepository, JpaSpecificationExecutor {
Optional findByBillNo(String billNo);
}
在src/main/java/com/example/archivefeesystem/service/impl目录下创建服务实现。以账单创建为例:
package com.example.archivefeesystem.service.impl;
import com.example.archivefeesystem.entity.FeeBill;
import com.example.archivefeesystem.entity.FeeBillDetail;
import com.example.archivefeesystem.entity.FeeItem;
import com.example.archivefeesystem.repository.FeeBillDetailRepository;
import com.example.archivefeesystem.repository.FeeBillRepository;
import com.example.archivefeesystem.repository.FeeItemRepository;
import com.example.archivefeesystem.service.FeeBillService;
import com.example.archivefeesystem.vo.BillCreateRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.UUID;
@Service
@RequiredArgsConstructor
public class FeeBillServiceImpl implements FeeBillService {
private final FeeBillRepository feeBillRepository;
private final FeeItemRepository feeItemRepository;
private final FeeBillDetailRepository feeBillDetailRepository;
@Override
@Transactional
public FeeBill createBill(BillCreateRequest request) {
// 1. 生成唯一账单号
String billNo = "BILL" + LocalDateTime.now().format(java.time.format.DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) + UUID.randomUUID().toString().substring(0, 6).toUpperCase();
// 2. 创建主账单
FeeBill bill = new FeeBill();
bill.setBillNo(billNo);
bill.setArchiveId(request.getArchiveId());
bill.setPayerName(request.getPayerName());
bill.setPayerContact(request.getPayerContact());
bill.setStatus(0); // 待支付
bill.setCreateTime(LocalDateTime.now());
// 3. 计算明细和总金额
BigDecimal totalAmount = BigDecimal.ZERO;
for (BillCreateRequest.DetailItem detailItem : request.getDetails()) {
FeeItem item = feeItemRepository.findById(detailItem.getFeeItemId())
.orElseThrow(() -> new RuntimeException("费用项目不存在: " + detailItem.getFeeItemId()));
FeeBillDetail detail = new FeeBillDetail();
detail.setFeeBill(bill);
detail.setFeeItem(item);
detail.setQuantity(detailItem.getQuantity());
detail.setUnitPrice(item.getUnitPrice());
BigDecimal subtotal = item.getUnitPrice().multiply(BigDecimal.valueOf(detailItem.getQuantity()));
detail.setSubtotal(subtotal);
detail.setRemark(detailItem.getRemark());
totalAmount = totalAmount.add(subtotal);
}
bill.setTotalAmount(totalAmount);
// 4. 保存(级联保存明细)
FeeBill savedBill = feeBillRepository.save(bill);
return savedBill;
}
}
在src/main/java/com/example/archivefeesystem/controller目录下创建REST API控制器。
package com.example.archivefeesystem.controller;
import com.example.archivefeesystem.entity.FeeBill;
import com.example.archivefeesystem.service.FeeBillService;
import com.example.archivefeesystem.vo.BillCreateRequest;
import com.example.archivefeesystem.vo.CommonResult;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.;
@RestController
@RequestMapping("/api/fee/bill")
@RequiredArgsConstructor
public class FeeBillController {
private final FeeBillService feeBillService;
@PostMapping("/create")
public CommonResult createBill(@Valid @RequestBody BillCreateRequest request) {
FeeBill bill = feeBillService.createBill(request);
return CommonResult.success(bill);
}
@GetMapping("/{billNo}")
public CommonResult getBillByNo(@PathVariable String billNo) {
// 查询逻辑,此处省略
return CommonResult.success(null);
}
}
进入Vue项目目录,安装Element Plus UI库和Axios。
cd archive-fee-frontend
npm install element-plus axios
在src/main.js中全局引入Element Plus。
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
const app = createApp(App)
app.use(router)
app.use(ElementPlus)
app.mount('app')
在src/views目录下创建BillCreate.vue。
创建档案收费账单
费用明细
¥ {{ detail.subtotal || '0.00' }}
-
+ 添加费用项
账单总金额: ¥ {{ totalAmount }}
提交账单
重置