feat: integrate analytics and fragment receipt improvements
This commit is contained in:
@@ -2,6 +2,11 @@ import { BadRequestException, ConflictException, Injectable, Optional } from '@n
|
||||
import { Prisma } from '@prisma/client';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { PhoneRoutingLookupService } from './phone-routing-lookup.service';
|
||||
import {
|
||||
detectDrainageContentWithRules,
|
||||
invalidateDrainageDetectionRuleCache,
|
||||
validateDrainageDetectionPattern,
|
||||
} from '../send-chain/drainage-content-detection';
|
||||
|
||||
export interface CreatePhoneSegmentDto {
|
||||
prefix: string;
|
||||
@@ -58,6 +63,23 @@ export interface CreateDrainageFieldDto {
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export interface UpsertDrainageDetectionRuleDto {
|
||||
code: string;
|
||||
name: string;
|
||||
category: 'url' | 'mobile' | 'landline';
|
||||
pattern: string;
|
||||
flags?: string;
|
||||
priority?: number;
|
||||
status?: string;
|
||||
description?: string;
|
||||
operatorId?: string;
|
||||
}
|
||||
|
||||
export interface TestDrainageDetectionDto {
|
||||
content: string;
|
||||
rule?: UpsertDrainageDetectionRuleDto;
|
||||
}
|
||||
|
||||
export interface CreateCommonReportFieldDto {
|
||||
drainageFieldId: string;
|
||||
reportType: 'signature' | 'drainage';
|
||||
@@ -374,6 +396,97 @@ export class DictionariesService {
|
||||
});
|
||||
}
|
||||
|
||||
listDrainageDetectionRules(query: { keyword?: string; status?: string } = {}) {
|
||||
const keyword = query.keyword?.trim();
|
||||
return this.prisma.drainageDetectionRule.findMany({
|
||||
where: {
|
||||
status: query.status && query.status !== 'all' ? query.status : { not: 'deleted' },
|
||||
OR: keyword ? [
|
||||
{ code: { contains: keyword, mode: 'insensitive' } },
|
||||
{ name: { contains: keyword, mode: 'insensitive' } },
|
||||
{ description: { contains: keyword, mode: 'insensitive' } },
|
||||
] : undefined,
|
||||
},
|
||||
orderBy: [{ priority: 'asc' }, { createdAt: 'asc' }],
|
||||
});
|
||||
}
|
||||
|
||||
async createDrainageDetectionRule(data: UpsertDrainageDetectionRuleDto) {
|
||||
this.validateDrainageDetectionRule(data);
|
||||
const created = await this.prisma.drainageDetectionRule.create({
|
||||
data: {
|
||||
code: data.code.trim().toUpperCase(),
|
||||
name: data.name.trim(),
|
||||
category: data.category,
|
||||
pattern: data.pattern,
|
||||
flags: data.flags ?? 'giu',
|
||||
priority: data.priority ?? 100,
|
||||
status: data.status ?? 'active',
|
||||
description: data.description?.trim() || null,
|
||||
},
|
||||
});
|
||||
invalidateDrainageDetectionRuleCache();
|
||||
await this.writeOperationLog(data.operatorId, 'drainage_detection_rule.create', 'drainage_detection_rule', created.id, { code: created.code });
|
||||
return created;
|
||||
}
|
||||
|
||||
async updateDrainageDetectionRule(id: string, data: UpsertDrainageDetectionRuleDto) {
|
||||
this.validateDrainageDetectionRule(data);
|
||||
const updated = await this.prisma.drainageDetectionRule.update({
|
||||
where: { id },
|
||||
data: {
|
||||
code: data.code.trim().toUpperCase(),
|
||||
name: data.name.trim(),
|
||||
category: data.category,
|
||||
pattern: data.pattern,
|
||||
flags: data.flags ?? 'giu',
|
||||
priority: data.priority ?? 100,
|
||||
status: data.status ?? 'active',
|
||||
description: data.description?.trim() || null,
|
||||
version: { increment: 1 },
|
||||
},
|
||||
});
|
||||
invalidateDrainageDetectionRuleCache();
|
||||
await this.writeOperationLog(data.operatorId, 'drainage_detection_rule.update', 'drainage_detection_rule', id, { code: updated.code, version: updated.version });
|
||||
return updated;
|
||||
}
|
||||
|
||||
async changeDrainageDetectionRuleStatus(id: string, data: DictionaryStatusDto) {
|
||||
const status = data.status === 'inactive' ? 'inactive' : 'active';
|
||||
const updated = await this.prisma.drainageDetectionRule.update({
|
||||
where: { id },
|
||||
data: { status, version: { increment: 1 } },
|
||||
});
|
||||
invalidateDrainageDetectionRuleCache();
|
||||
await this.writeOperationLog(data.operatorId, `drainage_detection_rule.${status}`, 'drainage_detection_rule', id, { reason: data.reason });
|
||||
return updated;
|
||||
}
|
||||
|
||||
async testDrainageDetection(data: TestDrainageDetectionDto) {
|
||||
if (!data.content?.trim()) throw new BadRequestException('测试短信内容不能为空');
|
||||
const rules = data.rule
|
||||
? [{
|
||||
id: 'preview',
|
||||
code: data.rule.code?.trim().toUpperCase() || 'PREVIEW',
|
||||
name: data.rule.name?.trim() || '预览规则',
|
||||
category: data.rule.category,
|
||||
pattern: data.rule.pattern,
|
||||
flags: data.rule.flags ?? 'giu',
|
||||
priority: data.rule.priority ?? 100,
|
||||
version: 1,
|
||||
}]
|
||||
: await this.prisma.drainageDetectionRule.findMany({ where: { status: 'active' }, orderBy: { priority: 'asc' } });
|
||||
if (data.rule) this.validateDrainageDetectionRule(data.rule);
|
||||
return detectDrainageContentWithRules(data.content, rules);
|
||||
}
|
||||
|
||||
private validateDrainageDetectionRule(data: UpsertDrainageDetectionRuleDto) {
|
||||
if (!data.code?.trim() || !data.name?.trim()) throw new BadRequestException('规则编码和名称不能为空');
|
||||
if (!['url', 'mobile', 'landline'].includes(data.category)) throw new BadRequestException('规则类型仅支持 URL、手机号或固话');
|
||||
if (data.status && !['active', 'inactive'].includes(data.status)) throw new BadRequestException('规则状态不正确');
|
||||
validateDrainageDetectionPattern(data.pattern, data.flags ?? 'giu');
|
||||
}
|
||||
|
||||
listCommonReportFields() {
|
||||
return this.prisma.commonReportField.findMany({
|
||||
include: { drainageField: true },
|
||||
|
||||
Reference in New Issue
Block a user