我们采用前后端分离架构,前端使用Vue 3 + TypeScript,后端使用Spring Boot 2.7,数据库选用MySQL 8.0。这种组合能确保系统的稳定性和可维护性。
前端:Vue 3.3 + Vite 5 + Element Plus 2.3 + Axios 1.6
后端:Spring Boot 2.7.15 + MyBatis Plus 3.5 + MySQL 8.0.33
工具:Maven 3.9.6 + Node.js 18.17 + Git 2.42
首先安装基础开发工具:
1. 安装Node.js(前端开发)
```bash 下载地址:https://nodejs.org/dist/v18.17.1/node-v18.17.1-x64.msi 安装后验证版本 node --version npm --version ```2. 安装Java开发环境
```bash 下载JDK 17:https://download.oracle.com/java/17/latest/jdk-17_windows-x64_bin.exe 设置环境变量 JAVA_HOME=C:\Program Files\Java\jdk-17 Path=%JAVA_HOME%\bin 验证安装 java -version ```3. 安装MySQL数据库
```bash 下载MySQL 8.0:https://dev.mysql.com/get/Downloads/MySQL-8.0/mysql-8.0.33-winx64.zip 初始化数据库 mysqld --initialize --console 记住初始密码,启动服务 net start mysql80 ```创建数据库和用户:
```sql CREATE DATABASE ledger_archive DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER 'ledger_user'@'localhost' IDENTIFIED BY 'Ledger@2024'; GRANT ALL PRIVILEGES ON ledger_archive. TO 'ledger_user'@'localhost'; FLUSH PRIVILEGES; USE ledger_archive; ```创建账簿主表:
```sql CREATE TABLE ledger_main ( id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID', ledger_code VARCHAR(50) NOT NULL UNIQUE COMMENT '账簿编号', ledger_name VARCHAR(100) NOT NULL COMMENT '账簿名称', ledger_type TINYINT NOT NULL COMMENT '账簿类型:1-现金日记账 2-银行日记账 3-总分类账', start_date DATE NOT NULL COMMENT '启用日期', end_date DATE COMMENT '截止日期', currency_code VARCHAR(3) DEFAULT 'CNY' COMMENT '币种', status TINYINT DEFAULT 1 COMMENT '状态:1-启用 2-停用', create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', INDEX idx_ledger_code (ledger_code), INDEX idx_status (status) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='账簿主表'; ```
创建凭证记录表:
```sql CREATE TABLE voucher_record ( id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID', voucher_no VARCHAR(50) NOT NULL COMMENT '凭证编号', ledger_id BIGINT NOT NULL COMMENT '账簿ID', voucher_date DATE NOT NULL COMMENT '凭证日期', summary VARCHAR(200) NOT NULL COMMENT '摘要', debit_amount DECIMAL(15,2) NOT NULL DEFAULT 0 COMMENT '借方金额', credit_amount DECIMAL(15,2) NOT NULL DEFAULT 0 COMMENT '贷方金额', balance DECIMAL(15,2) NOT NULL DEFAULT 0 COMMENT '余额', attachment_count INT DEFAULT 0 COMMENT '附件数量', operator_id BIGINT NOT NULL COMMENT '操作人ID', audit_status TINYINT DEFAULT 1 COMMENT '审核状态:1-待审核 2-已审核 3-已驳回', create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', FOREIGN KEY (ledger_id) REFERENCES ledger_main(id), INDEX idx_voucher_no (voucher_no), INDEX idx_ledger_date (ledger_id, voucher_date), INDEX idx_audit_status (audit_status) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='凭证记录表'; ```创建Spring Boot项目:
```bash 使用Spring Initializr创建项目 访问:https://start.spring.io/ 选择:Spring Boot 2.7.15, Java 17, Maven 依赖:Spring Web, MyBatis, MySQL Driver, Lombok 下载后解压到工作目录 ```配置application.yml:
```yaml server: port: 8080 servlet: context-path: /api spring: datasource: url: jdbc:mysql://localhost:3306/ledger_archive?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false username: ledger_user password: Ledger@2024 driver-class-name: com.mysql.cj.jdbc.Driver hikari: maximum-pool-size: 20 minimum-idle: 5 connection-timeout: 30000 mybatis-plus: configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0 logging: level: com.ledger.mapper: debug ```创建LedgerMain实体类:
```java package com.ledger.entity; import com.baomidou.mybatisplus.annotation.; import lombok.Data; import java.time.LocalDateTime; @Data @TableName("ledger_main") public class LedgerMain { @TableId(type = IdType.AUTO) private Long id; private String ledgerCode; private String ledgerName; private Integer ledgerType; private LocalDate startDate; private LocalDate endDate; private String currencyCode; private Integer status; @TableField(fill = FieldFill.INSERT) private LocalDateTime createTime; @TableField(fill = FieldFill.INSERT_UPDATE) private LocalDateTime updateTime; } ```创建VoucherRecord实体类:
```java package com.ledger.entity; import com.baomidou.mybatisplus.annotation.; import lombok.Data; import java.math.BigDecimal; import java.time.LocalDateTime; @Data @TableName("voucher_record") public class VoucherRecord { @TableId(type = IdType.AUTO) private Long id; private String voucherNo; private Long ledgerId; private LocalDate voucherDate; private String summary; private BigDecimal debitAmount; private BigDecimal creditAmount; private BigDecimal balance; private Integer attachmentCount; private Long operatorId; private Integer auditStatus; @TableField(fill = FieldFill.INSERT) private LocalDateTime createTime; } ```创建凭证服务接口:
```java package com.ledger.service; import com.baomidou.mybatisplus.extension.service.IService; import com.ledger.entity.VoucherRecord; import com.ledger.vo.VoucherVO; import java.time.LocalDate; public interface VoucherService extends IService实现凭证服务:
```java package com.ledger.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ledger.entity.VoucherRecord; import com.ledger.mapper.VoucherMapper; import com.ledger.service.VoucherService; import com.ledger.vo.VoucherVO; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.time.LocalDate; import java.time.format.DateTimeFormatter; @Service @RequiredArgsConstructor public class VoucherServiceImpl extends ServiceImpl创建Vue项目:
```bash 创建项目 npm create vue@latest ledger-frontend 选择配置:TypeScript, Router, Pinia, ESLint cd ledger-frontend npm install 安装UI库和工具 npm install element-plus axios dayjs npm install --save-dev @types/node ```配置vite.config.ts:
```typescript import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import { resolve } from 'path' export default defineConfig({ plugins: [vue()], resolve: { alias: { '@': resolve(__dirname, 'src') } }, server: { port: 3000, proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } } } }) ```创建凭证列表组件:
```vue创建API配置文件:
```typescript // src/api/voucher.ts import request from '@/utils/request' import type { VoucherItem, VoucherParams } from '@/types/voucher' export function getVoucherList(params: VoucherParams) { return request({ url: '/voucher/list', method: 'get', params }) } export function saveVoucher(data: VoucherItem) { return request({ url: '/voucher/save', method: 'post', data }) } export function auditVoucher(voucherId: number, auditStatus: number) { return request({ url: '/voucher/audit', method: 'post', data: { voucherId, auditStatus } }) } // src/utils/request.ts import axios from 'axios' import { ElMessage } from 'element-plus' import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios' const service: AxiosInstance = axios.create({ baseURL: '/api', timeout: 10000 }) service.interceptors.request.use( (config: AxiosRequestConfig) => { const token = localStorage.getItem('token') if (token && config.headers) { config.headers['Authorization'] = `Bearer