零售版文书档案系统从零搭建实战指南
系统架构与核心组件选型
零售行业文书档案系统需要处理销售单据、合同协议、资质证照等结构化与非结构化数据。我们采用前后端分离架构,前端使用Vue 3 + Element Plus,后端使用Spring Boot 2.7,数据库使用MySQL 8.0,文件存储使用MinIO对象存储。
技术栈版本要求
- JDK 17.0.2或更高版本
- Node.js 18.12.0或更高版本
- MySQL 8.0.32或更高版本
- Redis 7.0.8或更高版本
环境准备与依赖安装
后端环境配置
创建Spring Boot项目,在pom.xml中添加以下核心依赖:
```xml前端环境配置
创建Vue项目并安装必要依赖:
```bash npm create vue@latest retail-doc-system cd retail-doc-system npm install element-plus axios vue-router@4 vuex@4 ```数据库设计与初始化
核心表结构设计
执行以下SQL创建核心表:
```sql CREATE DATABASE retail_doc_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE retail_doc_db; CREATE TABLE document_category ( id BIGINT PRIMARY KEY AUTO_INCREMENT, category_code VARCHAR(50) NOT NULL UNIQUE, category_name VARCHAR(100) NOT NULL, parent_id BIGINT DEFAULT 0, sort_order INT DEFAULT 0, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, INDEX idx_parent_id (parent_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE document ( id BIGINT PRIMARY KEY AUTO_INCREMENT, doc_number VARCHAR(100) NOT NULL UNIQUE, doc_name VARCHAR(200) NOT NULL, category_id BIGINT NOT NULL, doc_type ENUM('CONTRACT', 'INVOICE', 'LICENSE', 'REPORT', 'OTHER') NOT NULL, store_code VARCHAR(50) NOT NULL, file_key VARCHAR(500) NOT NULL, file_size BIGINT NOT NULL, mime_type VARCHAR(100), status ENUM('DRAFT', 'ACTIVE', 'ARCHIVED', 'DELETED') DEFAULT 'DRAFT', metadata JSON, created_by VARCHAR(50), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, INDEX idx_category_id (category_id), INDEX idx_store_code (store_code), INDEX idx_status (status), INDEX idx_created_at (created_at) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE document_tag ( id BIGINT PRIMARY KEY AUTO_INCREMENT, document_id BIGINT NOT NULL, tag_name VARCHAR(50) NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_document_tag (document_id, tag_name), INDEX idx_tag_name (tag_name) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; ```初始化基础数据
插入必要的分类数据:
```sql INSERT INTO document_category (category_code, category_name, parent_id, sort_order) VALUES ('SALES', '销售单据', 0, 1), ('SALES_INVOICE', '销售发票', 1, 1), ('SALES_CONTRACT', '销售合同', 1, 2), ('FINANCE', '财务凭证', 0, 2), ('FINANCE_RECEIPT', '收款凭证', 4, 1), ('FINANCE_PAYMENT', '付款凭证', 4, 2), ('LEGAL', '法务文件', 0, 3), ('LEGAL_LICENSE', '资质证照', 7, 1), ('LEGAL_AGREEMENT', '合作协议', 7, 2); ```文件存储服务配置
MinIO安装与配置
使用Docker快速部署MinIO:
```bash docker run -d \ -p 9000:9000 \ -p 9001:9001 \ --name minio \ -v /mnt/data:/data \ -e "MINIO_ROOT_USER=admin" \ -e "MINIO_ROOT_PASSWORD=your_strong_password" \ minio/minio server /data --console-address ":9001" ```
在Spring Boot中配置MinIO客户端:
```yaml application.yml minio: endpoint: http://localhost:9000 access-key: admin secret-key: your_strong_password bucket-name: retail-documents ```创建MinIO配置类:
```java @Configuration public class MinioConfig { @Value("${minio.endpoint}") private String endpoint; @Value("${minio.access-key}") private String accessKey; @Value("${minio.secret-key}") private String secretKey; @Bean public MinioClient minioClient() { return MinioClient.builder() .endpoint(endpoint) .credentials(accessKey, secretKey) .build(); } } ```核心功能实现
文件上传服务
创建文件上传服务类:
```java @Service @Slf4j public class DocumentUploadService { @Value("${minio.bucket-name}") private String bucketName; @Autowired private MinioClient minioClient; public String uploadFile(MultipartFile file, String storeCode) throws Exception { // 生成唯一文件键 String originalFilename = file.getOriginalFilename(); String extension = originalFilename.substring(originalFilename.lastIndexOf(".")); String fileKey = storeCode + "/" + UUID.randomUUID() + extension; // 创建存储桶(如果不存在) boolean found = minioClient.bucketExists(BucketExistsArgs.builder() .bucket(bucketName) .build()); if (!found) { minioClient.makeBucket(MakeBucketArgs.builder() .bucket(bucketName) .build()); } // 上传文件 minioClient.putObject( PutObjectArgs.builder() .bucket(bucketName) .object(fileKey) .stream(file.getInputStream(), file.getSize(), -1) .contentType(file.getContentType()) .build() ); return fileKey; } public InputStream downloadFile(String fileKey) throws Exception { return minioClient.getObject( GetObjectArgs.builder() .bucket(bucketName) .object(fileKey) .build() ); } } ```文档管理控制器
实现RESTful API接口:
```java @RestController @RequestMapping("/api/documents") @RequiredArgsConstructor public class DocumentController { private final DocumentService documentService; private final DocumentUploadService uploadService; @PostMapping("/upload") public ApiResponse- > searchDocuments(
@RequestParam(required = false) String storeCode,
@RequestParam(required = false) String categoryCode,
@RequestParam(required = false) String keyword,
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate startDate,
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate endDate,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20") int size) {
Page
前端界面实现
文件上传组件
创建DocumentUpload.vue组件:
```vue
拖拽文件到此处或点击上传
支持上传PDF、Word、Excel、图片文件,单个文件不超过50MB
文档搜索组件
创建DocumentSearch.vue组件:
```vue