Files
lislgosms/api/src/dictionaries/dictionaries.service.ts
T

277 lines
8.5 KiB
TypeScript

import { BadRequestException, Injectable } from '@nestjs/common';
import { Prisma } from '@prisma/client';
import { PrismaService } from '../prisma/prisma.service';
export interface CreatePhoneSegmentDto {
prefix: string;
carrier: string;
province?: string;
city?: string;
}
export interface PhoneSegmentListQuery {
keyword?: string;
cursor?: string;
pageSize?: number;
}
export interface CreatePhoneCarrierRuleDto {
carrier: string;
pattern: string;
priority?: number;
status?: string;
remark?: string;
}
export interface CreateSensitiveWordDto {
word: string;
level?: string;
status?: string;
}
export interface CreateBlacklistDto {
tenantId?: string;
applicationId?: string;
phoneNumber: string;
reason?: string;
status?: string;
operatorId?: string;
}
export interface CreateDrainageFieldDto {
code: string;
name: string;
fieldType: string;
required?: boolean;
status?: string;
description?: string;
}
export interface DictionaryStatusDto {
status?: string;
operatorId?: string;
reason?: string;
}
export interface DictionaryListQuery {
tenantId?: string;
applicationId?: string;
keyword?: string;
status?: string;
}
@Injectable()
export class DictionariesService {
constructor(private readonly prisma: PrismaService) {}
async listPhoneSegments(query: PhoneSegmentListQuery = {}) {
const pageSize = Math.min(100, Math.max(1, Number(query.pageSize ?? 20)));
const keyword = query.keyword?.trim();
const items = await this.prisma.phoneSegment.findMany({
where: {
AND: [
query.cursor ? { prefix: { gt: query.cursor } } : {},
keyword ? {
OR: [
{ prefix: { startsWith: keyword } },
{ carrier: { contains: keyword } },
{ province: { contains: keyword } },
{ city: { contains: keyword } },
],
} : {},
],
},
orderBy: { prefix: 'asc' },
take: pageSize + 1,
});
const hasMore = items.length > pageSize;
const pageItems = hasMore ? items.slice(0, pageSize) : items;
return {
items: pageItems,
pageSize,
hasMore,
nextCursor: hasMore ? pageItems.at(-1)?.prefix ?? null : null,
};
}
createPhoneSegment(data: CreatePhoneSegmentDto) {
return this.prisma.phoneSegment.create({ data });
}
listPhoneCarrierRules() {
return this.prisma.phoneCarrierRule.findMany({ orderBy: [{ priority: 'asc' }, { createdAt: 'desc' }], take: 200 });
}
createPhoneCarrierRule(data: CreatePhoneCarrierRuleDto) {
if (!data.carrier || !data.pattern) {
throw new BadRequestException('carrier and pattern are required');
}
try {
new RegExp(data.pattern);
} catch {
throw new BadRequestException('pattern must be a valid regular expression');
}
return this.prisma.phoneCarrierRule.create({
data: {
carrier: data.carrier,
pattern: data.pattern,
priority: data.priority ?? 100,
status: data.status ?? 'active',
remark: data.remark,
},
});
}
listSensitiveWords(query: DictionaryListQuery = {}) {
return this.prisma.sensitiveWord.findMany({
where: {
status: query.status && query.status !== 'all' ? query.status : undefined,
OR: query.keyword ? [
{ word: { contains: query.keyword } },
{ level: { contains: query.keyword } },
] : undefined,
},
orderBy: { createdAt: 'desc' },
take: 200,
});
}
async createSensitiveWord(data: CreateSensitiveWordDto) {
const created = await this.prisma.sensitiveWord.create({
data: {
word: data.word,
level: data.level ?? 'block',
status: data.status ?? 'active',
},
});
await this.writeOperationLog(undefined, 'sensitive_word.create', 'sensitive_word', created.id, { word: data.word });
return created;
}
async changeSensitiveWordStatus(id: string, data: DictionaryStatusDto) {
const status = data.status ?? 'active';
const updated = await this.prisma.sensitiveWord.update({ where: { id }, data: { status } });
await this.writeOperationLog(data.operatorId, `sensitive_word.${status}`, 'sensitive_word', id, { reason: data.reason });
return updated;
}
listGlobalBlacklist(query: DictionaryListQuery = {}) {
return this.prisma.globalBlacklist.findMany({
where: {
status: query.status && query.status !== 'all' ? query.status : undefined,
OR: query.keyword ? [
{ phoneNumber: { contains: query.keyword } },
{ reason: { contains: query.keyword } },
] : undefined,
},
orderBy: { createdAt: 'desc' },
take: 200,
});
}
async createGlobalBlacklist(data: CreateBlacklistDto) {
const created = await this.prisma.globalBlacklist.create({
data: {
phoneNumber: data.phoneNumber,
reason: data.reason,
status: data.status ?? 'active',
},
});
await this.writeOperationLog(data.operatorId, 'global_blacklist.create', 'global_blacklist', created.id, {
phoneNumber: data.phoneNumber,
reason: data.reason,
});
return created;
}
async changeGlobalBlacklistStatus(id: string, data: DictionaryStatusDto) {
const status = data.status ?? 'active';
const updated = await this.prisma.globalBlacklist.update({ where: { id }, data: { status } });
await this.writeOperationLog(data.operatorId, `global_blacklist.${status}`, 'global_blacklist', id, { reason: data.reason });
return updated;
}
listEnterpriseBlacklist(query: DictionaryListQuery = {}) {
return this.prisma.enterpriseBlacklist.findMany({
where: {
tenantId: query.tenantId,
applicationId: query.applicationId,
status: query.status && query.status !== 'all' ? query.status : undefined,
OR: query.keyword ? [
{ phoneNumber: { contains: query.keyword } },
{ reason: { contains: query.keyword } },
{ tenant: { name: { contains: query.keyword } } },
{ application: { name: { contains: query.keyword } } },
] : undefined,
},
include: { tenant: true, application: true },
orderBy: { createdAt: 'desc' },
take: 200,
});
}
async createEnterpriseBlacklist(data: CreateBlacklistDto) {
if (!data.tenantId || !data.applicationId) {
throw new BadRequestException('tenantId and applicationId are required for application blacklist');
}
const application = await this.prisma.smsApplication.findUnique({
where: { id: data.applicationId },
select: { tenantId: true },
});
if (!application || application.tenantId !== data.tenantId) {
throw new BadRequestException('applicationId does not belong to tenantId');
}
const createData: Prisma.EnterpriseBlacklistUncheckedCreateInput = {
tenantId: data.tenantId,
applicationId: data.applicationId,
phoneNumber: data.phoneNumber,
reason: data.reason,
status: data.status ?? 'active',
};
const created = await this.prisma.enterpriseBlacklist.create({ data: createData });
await this.writeOperationLog(data.operatorId, 'enterprise_blacklist.create', 'enterprise_blacklist', created.id, {
tenantId: data.tenantId,
applicationId: data.applicationId,
phoneNumber: data.phoneNumber,
reason: data.reason,
});
return created;
}
async changeEnterpriseBlacklistStatus(id: string, data: DictionaryStatusDto) {
const status = data.status ?? 'active';
const updated = await this.prisma.enterpriseBlacklist.update({ where: { id }, data: { status } });
await this.writeOperationLog(data.operatorId, `enterprise_blacklist.${status}`, 'enterprise_blacklist', id, { reason: data.reason });
return updated;
}
listDrainageFields() {
return this.prisma.drainageField.findMany({ orderBy: { createdAt: 'desc' }, take: 200 });
}
createDrainageField(data: CreateDrainageFieldDto) {
return this.prisma.drainageField.create({
data: {
code: data.code,
name: data.name,
fieldType: data.fieldType,
required: data.required ?? false,
status: data.status ?? 'active',
description: data.description,
},
});
}
private writeOperationLog(userId: string | undefined, action: string, resource: string, resourceId: string, detail: Record<string, unknown>) {
return this.prisma.operationLog.create({
data: {
userId,
action,
resource,
resourceId,
detail: detail as Prisma.InputJsonValue,
},
});
}
}