Files
backend/architecture-design.md

802 lines
22 KiB
Markdown
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.
99# 云印享 - 付费打印服务系统架构设计
## 1. 项目概述
### 1.1 项目背景
构建一个提供付费打印服务的微信小程序系统,用户可通过小程序上传文档/图片/压缩包选择打印机并完成打印。系统包含用户端小程序、管理员端Windows客户端和后端服务。
### 1.2 核心功能需求
| 模块 | 功能点 |
|------|--------|
| 用户端小程序 | 上传文件(文档/图片/压缩包)、价目表展示、选择打印机、文件编辑(预览/水印/合并)、聊天记录选择文件、打印订单管理 |
| 管理员端 | 文库管理(上传资料供用户打印)、订单处理、打印机管理、用户管理 |
| 后端服务 | 用户认证、文件存储、订单处理、API网关 |
| Windows客户端 | 调用Windows API连接打印机、执行打印任务、文件预处理 |
### 1.3 技术栈
- **小程序端**Taro跨平台框架+ React技术栈
- **后端**Go + gRPC + MySQL + go-pay微信支付
- **Windows客户端**C# (.NET 8) + WPF
- **管理后台**React + Ant Design网页端
- **文件存储**:本地存储 + MinIO可选
- **通信**gRPC + WebSocket
---
## 2. 系统架构
### 2.1 整体架构图
```mermaid
flowchart TB
subgraph Client["客户端层"]
MiniApp["Taro微信小程序<br/>用户端"]
AdminWeb["React管理后台<br/>网页端"]
end
subgraph Backend["后端服务层"]
GRPC["gRPC Gateway<br/>:8080"]
Auth["认证服务"]
File["文件服务"]
Order["订单服务"]
Print["打印服务"]
Pay["支付服务<br/>go-pay"]
end
subgraph Data["数据层"]
MySQL["MySQL<br/>数据库"]
Redis["Redis<br/>缓存"]
MinIO["MinIO<br/>文件存储"]
end
subgraph PrintShop["打印店内部署"]
WinClient["C# WPF客户端<br/>打印管理程序"]
Printers["打印机列表"]
LocalFiles["本地文库"]
end
MiniApp -->|"HTTPS/gRPC"| GRPC
AdminWeb -->|"HTTPS/gRPC"| GRPC
WinClient -->|"gRPC| WebSocket"| GRPC
GRPC --> Auth
GRPC --> File
GRPC --> Order
GRPC --> Print
GRPC --> Pay
Auth --> Redis
Order --> MySQL
File --> MinIO
File --> MySQL
Pay --> WeChat["微信支付"]
WinClient --> Printers
WinClient --> LocalFiles
```
### 2.2 部署架构
```mermaid
flowchart LR
subgraph Internet["互联网"]
User["用户<br/>微信小程序"]
end
subgraph Cloud["云服务器"]
API_Server["Go API Server"]
DB["MySQL + Redis"]
File_Storage["文件存储服务"]
end
subgraph Local["打印店本地"]
PC["Windows电脑<br/>C# WPF客户端"]
Printer["打印机"]
end
User -->|"上传文件/下单"| Cloud
Cloud -->|"转发打印任务"| PC
PC -->|"执行打印"| Printer
```
---
## 3. 模块详细设计
### 3.1 微信小程序端
#### 3.1.1 页面结构
```
pages/
├── index/ # 首页
│ ├──价目表展示
│ ├──快捷上传入口
│ └──最近的打印历史
├── upload/ # 文件上传
│ ├──文件选择(支持聊天记录)
│ ├──上传进度
│ └──文件预览
├── editor/ # 文件编辑
│ ├──PDF预览
│ ├──水印设置
│ ├──页面合并
│ └──打印参数设置
├── printer/ # 打印机选择
│ ├──打印机列表
│ ├──打印机状态
│ └──价格显示
├── order/ # 订单管理
│ ├──订单列表
│ ├──订单详情
│ └──支付
├── library/ # 文库(用户浏览)
│ ├──分类列表
│ ├──文件搜索
│ └──文件详情
└── my/ # 个人中心
├──打印记录
├──账户余额
└──设置
```
#### 3.1.2 核心页面流程
```mermaid
flowchart TD
Start["首页"] --> Upload{"选择上传方式"}
Upload -->|"本地上传"| SelectFile["选择文件"]
Upload -->|"聊天记录"| ChatFile["从聊天选择"]
SelectFile --> Preview["文件预览"]
ChatFile --> Preview
Preview --> Edit{"需要编辑?"}
Edit -->|"是"| Editor["编辑页面"]
Edit -->|"否"| SelectPrinter["选择打印机"]
Editor --> SelectPrinter
SelectPrinter --> Confirm["确认订单"]
Confirm --> Pay["支付"]
Pay --> Success["下单成功"]
Success --> Notify["通知打印店"]
```
#### 3.1.3 文件编辑功能设计
| 功能 | 实现方式 | 说明 |
|------|----------|------|
| PDF预览 | pdf.js | 轻量级PDF预览支持缩放、翻页 |
| 添加水印 | 前端Canvas + 后端PDF处理 | 支持文字水印、位置、透明度 |
| 页面合并 | pdf-lib | 合并多个PDF页面 |
| 打印参数 | 份数、纸张大小、单双面、彩色/黑白 | 传递给Windows客户端 |
### 3.2 C# WPF打印客户端
#### 3.2.1 核心模块
```csharp
// 项目结构
CloudPrint.Client/
App.xaml #
Services/
PrintService.cs # Windows API
FileService.cs #
GrpcService.cs # gRPC通信服务
WebSocketService.cs # WebSocket备用通信
ViewModels/
MainViewModel.cs #
PrintQueueViewModel.cs #
LibraryViewModel.cs #
SettingsViewModel.cs #
Views/
MainWindow.xaml
PrintQueueView.xaml
LibraryView.xaml
SettingsView.xaml
Models/
PrintJob.cs
Printer.cs
Document.cs
Helpers/
WindowsPrintApi.cs # Windows打印API封装
PdfProcessor.cs # PDF处理
```
#### 3.2.2 Windows打印API集成
使用 P/Invoke 调用 Windows Print Spooler API
- `OpenPrinter`
- `StartDocPrinter`
- `StartPagePrinter`
- `WritePrinter`
- `EndPagePrinter`
- `EndDocPrinter`
- `ClosePrinter`
```csharp
// 核心打印服务类
public class PrintService
{
// 获取系统打印机列表
public List<PrinterInfo> GetPrinters();
// 获取打印机状态
public PrinterStatus GetPrinterStatus(string printerName);
// 提交打印任务
public string SubmitPrintJob(PrintJob job);
// 取消打印任务
public bool CancelPrintJob(string jobId);
// 获取打印任务状态
public PrintJobStatus GetJobStatus(string jobId);
}
```
#### 3.2.3 gRPC通信
```csharp
// 与后端gRPC服务通信
public class GrpcService
{
// 连接gRPC服务器
public async Task ConnectAsync(string address);
// 订阅打印任务流
public async Task SubscribePrintJobsAsync(string printerName);
// 发送状态更新
public async Task SendStatusUpdateAsync(PrintJobStatus status);
}
```
### 3.3 后端服务Go + gRPC + go-pay
#### 3.3.1 项目结构
```text
cloudprint-backend/
├── cmd/
│ └── server/
│ └── main.go # 入口
├── config/
│ └── config.go # 配置
├── internal/
│ ├── grpc/ # gRPC服务
│ │ ├── server.go # gRPC服务器入口
│ │ ├── interceptor/ # 拦截器
│ │ └── service/ # 服务实现
│ │ ├── auth.go
│ │ ├── file.go
│ │ ├── order.go
│ │ ├── print.go
│ │ └── library.go
│ ├── model/ # 数据模型
│ ├── repository/ # 数据访问
│ └── service/ # 业务逻辑
├── pkg/
│ ├── jwt/ # JWT认证
│ ├── pay/ # 支付服务 (go-pay)
│ └── upload/ # 文件上传
├── proto/ # Protobuf定义
│ ├── cloudprint.proto
│ └── generated/ # 生成的代码
├── scripts/
│ └── db.sql # 数据库脚本
└── go.mod
```
#### 3.3.2 gRPC 服务设计
**认证服务 (AuthService)**
```protobuf
service AuthService {
rpc Login(LoginRequest) returns (LoginResponse);
rpc RefreshToken(RefreshRequest) returns (RefreshResponse);
rpc Logout(LogoutRequest) returns (Empty);
}
```
**文件服务 (FileService)**
```protobuf
service FileService {
rpc Upload(stream UploadRequest) returns (UploadResponse);
rpc GetFile(GetFileRequest) returns (stream FileData);
rpc DeleteFile(DeleteRequest) returns (Empty);
rpc AddWatermark(WatermarkRequest) returns (WatermarkResponse);
}
```
**订单服务 (OrderService)**
```protobuf
service OrderService {
rpc CreateOrder(CreateOrderRequest) returns (OrderResponse);
rpc GetOrderList(GetOrderListRequest) returns (OrderListResponse);
rpc GetOrder(GetOrderRequest) returns (OrderResponse);
rpc UpdateOrderStatus(UpdateStatusRequest) returns (Empty);
rpc PayOrder(PayRequest) returns (PayResponse);
}
```
**打印服务 (PrintService)**
```protobuf
service PrintService {
rpc GetPrinters(Empty) returns (PrinterListResponse);
rpc GetPrinterStatus(PrinterRequest) returns (PrinterStatusResponse);
rpc SubmitPrintJob(PrintJobRequest) returns (PrintJobResponse);
rpc CancelPrintJob(CancelRequest) returns (Empty);
rpc SubscribeJobs(SubscriberRequest) returns (stream PrintJob);
rpc ReportJobStatus(JobStatusReport) returns (Empty);
}
```
**文库服务 (LibraryService)**
```protobuf
service LibraryService {
rpc GetCategories(Empty) returns (CategoryListResponse);
rpc GetFiles(GetFilesRequest) returns (FileListResponse);
rpc UploadFile(stream UploadRequest) returns (UploadResponse);
rpc DeleteFile(DeleteRequest) returns (Empty);
}
```
**支付服务 (PayService)**
```protobuf
service PayService {
rpc CreateWxPayOrder(WxPayRequest) returns (WxPayResponse);
rpc QueryWxPayStatus(QueryRequest) returns (PayStatusResponse);
rpc HandleWxPayCallback(CallbackRequest) returns (CallbackResponse);
}
```
#### 3.3.3 gRPC 通信流程
```mermaid
sequenceDiagram
participant CSharp as C# WPF客户端
participant Go as Go gRPC服务器
participant DB as MySQL/Redis
CSharp->>Go: 连接gRPC服务 (认证)
Go-->>CSharp: 连接成功
CSharp->>Go: 注册打印机 (SubscribeJobs)
Go->>DB: 查询待打印任务
DB-->>Go: 任务列表
loop 等待新任务
Go-->>CSharp: 推送新打印任务 (stream)
CSharp->>CSharp: 处理文件/调用打印API
CSharp->>Go: 上报状态 (ReportJobStatus)
Go->>DB: 更新任务状态
end
```
### 3.4 数据库设计
#### 3.4.1 ER 图
```mermaid
erDiagram
User ||--o{ Order : places
User {
int id PK
string openid UK
string nickname
decimal balance
datetime created_at
}
Order ||--|{ OrderItem : contains
Order ||--o{ Payment : has
Order {
int id PK
string order_no UK
int user_id FK
int status
decimal total_amount
datetime created_at
}
OrderItem {
int id PK
int order_id FK
int file_id FK
string printer_name
int copies
bool color
bool duplex
string page_range
decimal price
}
Payment {
int id PK
string order_no UK
int pay_status
string transaction_id
datetime paid_at
}
File {
int id PK
string file_name
string file_path
string file_type
int file_size
string uploader_type
int uploader_id
datetime created_at
}
Library |||--|{ LibraryFile : contains
Library {
int id PK
string name
string description
int sort_order
}
LibraryFile ||--o{ File : references
LibraryFile {
int id PK
int library_id FK
int file_id FK
string title
string description
}
Printer |||--o{ PrintJob : receives
Printer {
int id PK
string name UK
string display_name
bool is_active
string api_key UK
}
PrintJob {
int id PK
string job_no UK
int printer_id FK
int order_item_id FK
string status
int progress
datetime started_at
datetime completed_at
}
PriceList {
int id PK
string name
string type
decimal price
string unit
bool is_active
}
```
#### 3.4.2 主要表结构
```sql
-- 用户表
CREATE TABLE `user` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`openid` VARCHAR(64) UNIQUE NOT NULL COMMENT '微信OpenID',
`nickname` VARCHAR(128) DEFAULT '',
`avatar` VARCHAR(512) DEFAULT '',
`balance` DECIMAL(10,2) DEFAULT 0.00 COMMENT '余额',
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
-- 管理员表
CREATE TABLE `admin` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`username` VARCHAR(64) UNIQUE NOT NULL,
`password_hash` VARCHAR(256) NOT NULL,
`nickname` VARCHAR(128) DEFAULT '',
`role` TINYINT DEFAULT 1 COMMENT '1:普通管理员 2:超级管理员',
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- 打印机表
CREATE TABLE `printer` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(128) UNIQUE NOT NULL COMMENT '系统打印机名',
`display_name` VARCHAR(128) NOT NULL COMMENT '显示名称',
`api_key` VARCHAR(64) UNIQUE NOT NULL COMMENT '客户端认证Key',
`is_active` TINYINT DEFAULT 1,
`status` TINYINT DEFAULT 0 COMMENT '0:空闲 1:打印中 2:离线',
`last_heartbeat` DATETIME,
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- 订单表
CREATE TABLE `order` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`order_no` VARCHAR(32) UNIQUE NOT NULL COMMENT '订单号',
`user_id` INT NOT NULL,
`status` TINYINT DEFAULT 0 COMMENT '0:待支付 1:已支付 2:打印中 3:已完成 4:已取消',
`total_amount` DECIMAL(10,2) NOT NULL,
`remark` VARCHAR(512) DEFAULT '',
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP,
`paid_at` DATETIME,
FOREIGN KEY (`user_id`) REFERENCES `user`(`id`)
);
-- 订单明细表
CREATE TABLE `order_item` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`order_id` INT NOT NULL,
`file_id` INT NOT NULL,
`printer_id` INT,
`copies` INT DEFAULT 1,
`color` TINYINT DEFAULT 0,
`duplex` TINYINT DEFAULT 0,
`page_range` VARCHAR(64) DEFAULT 'all',
`price` DECIMAL(10,2) NOT NULL,
FOREIGN KEY (`order_id`) REFERENCES `order`(`id`),
FOREIGN KEY (`file_id`) REFERENCES `file`(`id`)
);
-- 文件表
CREATE TABLE `file` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`file_name` VARCHAR(256) NOT NULL,
`file_path` VARCHAR(512) NOT NULL,
`file_type` VARCHAR(32) NOT NULL COMMENT 'pdf/doc/docx/xls/xlsx/ppt/pptx/txt/jpg/png/zip',
`file_size` BIGINT NOT NULL COMMENT '字节',
`uploader_type` VARCHAR(16) NOT NULL COMMENT 'user/admin/system',
`uploader_id` INT NOT NULL,
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- 文库分类表
CREATE TABLE `library_category` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(128) NOT NULL,
`icon` VARCHAR(128) DEFAULT '',
`sort_order` INT DEFAULT 0,
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- 文库文件表
CREATE TABLE `library_file` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`category_id` INT NOT NULL,
`file_id` INT NOT NULL,
`title` VARCHAR(256) NOT NULL,
`description` VARCHAR(512) DEFAULT '',
`page_count` INT DEFAULT 0,
`download_count` INT DEFAULT 0,
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (`category_id`) REFERENCES `library_category`(`id`),
FOREIGN KEY (`file_id`) REFERENCES `file`(`id`)
);
-- 打印任务表
CREATE TABLE `print_job` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`job_no` VARCHAR(32) UNIQUE NOT NULL,
`printer_id` INT NOT NULL,
`order_item_id` INT NOT NULL,
`status` VARCHAR(16) DEFAULT 'pending' COMMENT 'pending/printing/completed/failed/cancelled',
`progress` INT DEFAULT 0,
`error_msg` VARCHAR(512) DEFAULT '',
`started_at` DATETIME,
`completed_at` DATETIME,
FOREIGN KEY (`printer_id`) REFERENCES `printer`(`id`)
);
-- 价目表
CREATE TABLE `price_list` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(128) NOT NULL COMMENT '项目名称',
`type` VARCHAR(32) NOT NULL COMMENT '纸张类型: A4/A3/照片纸',
`price` DECIMAL(10,2) NOT NULL COMMENT '单价',
`unit` VARCHAR(16) NOT NULL COMMENT '单位: 页/张/份',
`color_enabled` TINYINT DEFAULT 1 COMMENT '是否支持彩色',
`is_active` TINYINT DEFAULT 1,
`sort_order` INT DEFAULT 0,
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- 支付记录表
CREATE TABLE `payment` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`order_no` VARCHAR(32) UNIQUE NOT NULL,
`pay_type` VARCHAR(16) DEFAULT 'wechat' COMMENT 'wechat/balance',
`pay_status` TINYINT DEFAULT 0 COMMENT '0:待支付 1:支付中 2:成功 3:失败',
`transaction_id` VARCHAR(64) DEFAULT '',
`paid_at` DATETIME,
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP
);
```
---
## 4. 核心流程
### 4.1 用户打印流程
```mermaid
sequenceDiagram
participant User as 用户
participant MiniApp as 微信小程序
participant Backend as Go后端
participant Windows as Windows客户端
participant Printer as 打印机
User->>MiniApp: 选择文件上传
MiniApp->>MiniApp: 文件预览/编辑
User->>MiniApp: 确认打印参数
MiniApp->>Backend: 创建订单
Backend-->>MiniApp: 订单信息
User->>MiniApp: 支付订单
MiniApp->>Backend: 支付回调
Backend->>Windows: WebSocket推送打印任务
Windows->>Backend: 下载文件
Windows->>Windows: PDF预处理
Windows->>Printer: 发送打印指令
Printer-->>Windows: 打印中
Windows->>Backend: 更新任务状态
Backend->>MiniApp: 推送状态更新
Printer-->>User: 输出打印件
```
### 4.2 管理员上传文库流程
```mermaid
flowchart TD
Admin["管理员登录Windows客户端"] --> Navigate["进入文库管理"]
Navigate --> SelectCategory["选择分类"]
SelectCategory --> Upload["上传文件"]
Upload --> SetInfo["设置文件标题描述"]
SetInfo --> Confirm["确认上传"]
Confirm --> Save["保存到数据库"]
Save --> Sync["同步到云端"]
Sync --> Success["上传成功"]
```
---
## 5. 文件处理
### 5.1 支持的文件格式
| 类型 | 格式 | 处理方式 |
|------|------|----------|
| 文档 | doc, docx, txt | 转为PDF后打印 |
| 表格 | xls, xlsx | 转为PDF后打印 |
| 演示 | ppt, pptx | 转为PDF后打印 |
| 图片 | jpg, png, bmp, gif | 直接打印或转PDF |
| 压缩包 | zip, rar | 解压后处理内部文件 |
| PDF | pdf | 直接打印 |
### 5.2 C#端文件转换
使用以下库/工具进行文件转换:
- **Word/Excel/PowerPoint**: Microsoft Office COM Automation 或 LibreOffice Headless
- **图片**: System.Drawing
- **压缩包**: SharpCompress
- **PDF合并/水印**: PDFsharp 或 iTextSharp
---
## 6. 安全设计
### 6.1 认证授权
- **用户端**: 微信 OAuth2.0 获取 OpenID
- **管理后台**: 用户名密码 + JWT Token
- **打印客户端**: API Key 认证 + gRPC TLS加密连接
### 6.2 数据安全
- 文件上传: 大小限制 (单文件 50MB)
- 文件类型白名单验证
- SQL注入防护: 使用参数化查询
- XSS防护: 输出编码
- gRPC传输加密: TLS 1.3
### 6.3 打印客户端认证
gRPC认证流程
1. 客户端使用API Key获取临时凭证
2. 使用凭证建立gRPC TLS连接
3. 服务端验证客户端身份
4. 建立长连接后订阅打印任务流
---
## 7. 开发计划
### 7.1 项目模块划分
1. **cloudprint-miniapp** - Taro微信小程序用户端
2. **cloudprint-backend** - Go + gRPC 后端服务
3. **cloudprint-client** - C# (.NET 8) + WPF 打印客户端
4. **cloudprint-admin** - React 管理后台网页端
5. **cloudprint-proto** - Protobuf 定义共享库
### 7.2 开发顺序
```
1. cloudprint-proto (公共协议定义)
├── 定义所有 gRPC 服务和消息类型
├── 生成 Go、C#、TypeScript 代码
2. cloudprint-backend (Go + gRPC + go-pay)
├── 数据库设计与初始化
├── 基础框架搭建
├── 认证模块 (JWT)
├── 文件服务
├── 订单服务
├── 打印服务
├── 文库服务
└── 微信支付集成 (go-pay)
3. cloudprint-client (C# WPF)
├── 项目初始化 (.NET 8)
├── 打印服务开发 (P/Invoke Windows API)
├── gRPC 通信 (Grpc.Net)
├── WebSocket 通信 (用于推送)
├── 文件处理
└── 文库管理
4. cloudprint-admin (React + Ant Design)
├── 项目初始化
├── 登录认证
├── 订单管理
├── 用户管理
├── 价目表管理
└── 数据统计
5. cloudprint-miniapp (Taro)
├── 项目初始化 (@tarojs/cli)
├── 用户端页面开发
├── 文件上传与编辑
├── 订单流程
└── 与后端 gRPC 对接
```
---
## 8. 部署方案
### 8.1 云服务器要求
- **配置**: 2核4G起步
- **系统**: Ubuntu 22.04 / CentOS 8
- **数据库**: MySQL 8.0
- **缓存**: Redis 6.0
- **文件存储**: 本地磁盘或 MinIO
### 8.2 打印店客户端部署
1. 在打印店电脑安装 Windows 10/11 + .NET 8 Runtime
2. 运行 C# WPF 客户端程序,配置服务器地址
3. 登录管理员账号
4. 配置打印机
5. 保持客户端在线
---
## 9. 风险与注意事项
| 风险 | 应对措施 |
|------|----------|
| 文件格式兼容 | C#客户端集成LibreOffice转换 |
| 打印质量差异 | 客户端提供打印参数调试 |
| 大文件传输 | 分片上传、断点续传 |
| 客户端掉线 | gRPC心跳检测、自动重连 |
| 支付安全 | 微信支付官方SDK (go-pay) |
| 并发打印 | 任务队列、打印机组 |