123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313 |
- import { Injectable, inject } from '@angular/core';
- import { HttpClient } from '@angular/common/http';
- import { Observable, of } from 'rxjs';
- import { catchError, map } from 'rxjs/operators';
- // 支付凭证识别结果接口
- export interface PaymentVoucherRecognitionResult {
- success: boolean;
- amount?: number;
- paymentDate?: Date;
- paymentMethod?: string;
- payerName?: string;
- payerAccount?: string;
- receiverName?: string;
- receiverAccount?: string;
- transactionNumber?: string;
- confidence: number;
- extractedText?: string;
- error?: string;
- }
- // 支付凭证文件信息接口
- export interface PaymentVoucherFile {
- id: string;
- fileName: string;
- fileType: string;
- fileSize: number;
- fileUrl: string;
- previewUrl?: string;
- uploadDate: Date;
- }
- @Injectable({
- providedIn: 'root'
- })
- export class PaymentVoucherRecognitionService {
- private http = inject(HttpClient);
- /**
- * 识别支付凭证图片或PDF文件
- * @param file 支付凭证文件
- * @returns 识别结果的可观察对象
- */
- recognizePaymentVoucher(file: File): Observable<PaymentVoucherRecognitionResult> {
- // 检查文件类型
- if (!this.isSupportedFileType(file.type)) {
- return of({
- success: false,
- confidence: 0,
- error: '不支持的文件类型。请上传图片(JPG, PNG, GIF)或PDF文件。'
- });
- }
- // 检查文件大小(限制为10MB)
- if (file.size > 10 * 1024 * 1024) {
- return of({
- success: false,
- confidence: 0,
- error: '文件大小不能超过10MB。'
- });
- }
- // 在实际应用中,这里会调用OCR API服务
- // 目前先模拟识别过程
- return this.simulateRecognition(file);
- }
- /**
- * 批量识别支付凭证
- * @param files 支付凭证文件数组
- * @returns 批量识别结果的可观察对象
- */
- async recognizePaymentVouchers(files: File[]): Promise<PaymentVoucherRecognitionResult[]> {
- const recognitionPromises = files.map(file =>
- this.recognizePaymentVoucher(file).toPromise()
- );
- const results = await Promise.all(recognitionPromises);
- return results.filter(result => result !== undefined) as PaymentVoucherRecognitionResult[];
- }
- /**
- * 检查是否支持的文件类型
- * @param fileType 文件MIME类型
- */
- private isSupportedFileType(fileType: string): boolean {
- const supportedTypes = [
- 'image/jpeg',
- 'image/png',
- 'image/gif',
- 'application/pdf'
- ];
- return supportedTypes.includes(fileType);
- }
- /**
- * 模拟支付凭证识别过程
- * @param file 支付凭证文件
- */
- private simulateRecognition(file: File): Observable<PaymentVoucherRecognitionResult> {
- return new Observable(observer => {
- // 模拟处理延迟
- setTimeout(() => {
- try {
- // 根据文件名和类型生成模拟识别结果
- const fileName = file.name.toLowerCase();
-
- // 模拟不同的识别场景
- if (fileName.includes('alipay') || fileName.includes('支付宝')) {
- observer.next(this.generateAlipayResult(file));
- } else if (fileName.includes('wechat') || fileName.includes('微信')) {
- observer.next(this.generateWechatResult(file));
- } else if (fileName.includes('bank') || fileName.includes('银行')) {
- observer.next(this.generateBankTransferResult(file));
- } else {
- observer.next(this.generateGenericResult(file));
- }
-
- observer.complete();
- } catch (error) {
- observer.next({
- success: false,
- confidence: 0,
- error: `识别过程中发生错误: ${error}`
- });
- observer.complete();
- }
- }, 2000); // 2秒延迟模拟处理时间
- });
- }
- /**
- * 生成支付宝支付凭证识别结果
- */
- private generateAlipayResult(file: File): PaymentVoucherRecognitionResult {
- const amount = Math.round(Math.random() * 10000 + 1000); // 1000-11000之间的随机金额
- const confidence = Math.random() * 0.3 + 0.7; // 70%-100%的置信度
-
- return {
- success: true,
- amount,
- paymentDate: new Date(),
- paymentMethod: '支付宝',
- payerName: '付款人***',
- payerAccount: 'alipay***@xxx.com',
- receiverName: '收款人***',
- receiverAccount: 'alipay***@xxx.com',
- transactionNumber: `ALP${Date.now().toString().slice(-8)}`,
- confidence,
- extractedText: `支付宝交易凭证 金额: ${amount}元 交易时间: ${new Date().toLocaleString()}`
- };
- }
- /**
- * 生成微信支付凭证识别结果
- */
- private generateWechatResult(file: File): PaymentVoucherRecognitionResult {
- const amount = Math.round(Math.random() * 5000 + 500); // 500-5500之间的随机金额
- const confidence = Math.random() * 0.3 + 0.7; // 70%-100%的置信度
-
- return {
- success: true,
- amount,
- paymentDate: new Date(),
- paymentMethod: '微信支付',
- payerName: '微信用户***',
- payerAccount: 'wxid_***',
- receiverName: '商户***',
- receiverAccount: 'mch_***',
- transactionNumber: `WX${Date.now().toString().slice(-8)}`,
- confidence,
- extractedText: `微信支付凭证 金额: ${amount}元 交易时间: ${new Date().toLocaleString()}`
- };
- }
- /**
- * 生成银行转账凭证识别结果
- */
- private generateBankTransferResult(file: File): PaymentVoucherRecognitionResult {
- const amount = Math.round(Math.random() * 20000 + 2000); // 2000-22000之间的随机金额
- const confidence = Math.random() * 0.2 + 0.8; // 80%-100%的置信度
-
- return {
- success: true,
- amount,
- paymentDate: new Date(),
- paymentMethod: '银行转账',
- payerName: '付款人***',
- payerAccount: '6222********1234',
- receiverName: '收款人***',
- receiverAccount: '6222********5678',
- transactionNumber: `BANK${Date.now().toString().slice(-8)}`,
- confidence,
- extractedText: `银行转账凭证 金额: ${amount}元 交易时间: ${new Date().toLocaleString()}`
- };
- }
- /**
- * 生成通用支付凭证识别结果
- */
- private generateGenericResult(file: File): PaymentVoucherRecognitionResult {
- const amount = Math.round(Math.random() * 15000 + 1000); // 1000-16000之间的随机金额
- const confidence = Math.random() * 0.4 + 0.6; // 60%-100%的置信度
-
- const methods = ['支付宝', '微信支付', '银行转账', '现金'];
- const paymentMethod = methods[Math.floor(Math.random() * methods.length)];
-
- return {
- success: true,
- amount,
- paymentDate: new Date(),
- paymentMethod,
- payerName: '付款人***',
- receiverName: '收款人***',
- confidence,
- extractedText: `支付凭证 金额: ${amount}元 支付方式: ${paymentMethod} 交易时间: ${new Date().toLocaleString()}`
- };
- }
- /**
- * 验证识别结果的可信度
- * @param result 识别结果
- * @param expectedAmount 预期金额(可选)
- */
- validateRecognitionResult(
- result: PaymentVoucherRecognitionResult,
- expectedAmount?: number
- ): { isValid: boolean; reasons: string[] } {
- const reasons: string[] = [];
-
- if (!result.success) {
- reasons.push('识别失败');
- return { isValid: false, reasons };
- }
-
- if (result.confidence < 0.7) {
- reasons.push(`置信度过低: ${(result.confidence * 100).toFixed(1)}%`);
- }
-
- if (!result.amount || result.amount <= 0) {
- reasons.push('金额识别无效');
- }
-
- if (expectedAmount && result.amount && Math.abs(result.amount - expectedAmount) > expectedAmount * 0.1) {
- reasons.push(`识别金额与预期金额差异过大`);
- }
-
- if (!result.paymentDate) {
- reasons.push('支付日期识别失败');
- }
-
- if (!result.paymentMethod) {
- reasons.push('支付方式识别失败');
- }
-
- return {
- isValid: reasons.length === 0,
- reasons
- };
- }
- /**
- * 格式化金额显示
- * @param amount 金额
- */
- formatAmount(amount: number): string {
- return new Intl.NumberFormat('zh-CN', {
- style: 'currency',
- currency: 'CNY'
- }).format(amount);
- }
- /**
- * 获取支持的文件类型描述
- */
- getSupportedFileTypesDescription(): string {
- return '支持的文件类型: JPG, PNG, GIF图片或PDF文件,最大10MB';
- }
- /**
- * 获取支持的文件类型列表
- */
- getSupportedFileTypes(): string[] {
- return [
- 'image/jpeg',
- 'image/png',
- 'image/gif',
- 'application/pdf'
- ];
- }
- /**
- * 验证文件
- * @param file 要验证的文件
- */
- validateFile(file: File): { isValid: boolean; error?: string } {
- if (!this.isSupportedFileType(file.type)) {
- return {
- isValid: false,
- error: '不支持的文件类型。请上传图片(JPG, PNG, GIF)或PDF文件。'
- };
- }
- if (file.size > 10 * 1024 * 1024) {
- return {
- isValid: false,
- error: '文件大小不能超过10MB。'
- };
- }
- return { isValid: true };
- }
- }
|