feat: improve operations diagnostics and channel management

This commit is contained in:
hectorzhao
2026-08-09 14:27:19 +08:00
parent 44352aeb2f
commit 4724b9db6a
65 changed files with 1211 additions and 293 deletions
@@ -19,6 +19,11 @@ import {
export class DictionariesController {
constructor(private readonly dictionaries: DictionariesService) {}
@Get('administrative-regions')
listAdministrativeRegions() {
return this.dictionaries.listAdministrativeRegions();
}
@Get('phone-segments')
listPhoneSegments(
@Query('keyword') keyword?: string,
@@ -58,6 +58,28 @@ function createPrismaMock() {
}
describe('DictionariesService', () => {
it('builds the enterprise province and city library from distinct real phone segment regions', async () => {
const prisma = createPrismaMock();
prisma.phoneSegment.findMany.mockResolvedValue([
{ province: '山东', city: '青岛' },
{ province: '山东', city: '济南' },
{ province: '山东', city: '济南' },
{ province: '江苏', city: '苏州' },
{ province: ' ', city: '无效' },
]);
const service = new DictionariesService(prisma as never);
await expect(service.listAdministrativeRegions()).resolves.toEqual([
{ province: '江苏', cities: ['苏州'] },
{ province: '山东', cities: ['济南', '青岛'] },
]);
expect(prisma.phoneSegment.findMany).toHaveBeenCalledWith({
where: { province: { not: null } },
select: { province: true, city: true },
distinct: ['province', 'city'],
});
});
it('deletes a phone segment from the real dictionary table', async () => {
const prisma = createPrismaMock();
const service = new DictionariesService(prisma as never);
@@ -111,6 +111,27 @@ export class DictionariesService {
@Optional() private readonly phoneRoutingLookup?: PhoneRoutingLookupService,
) {}
async listAdministrativeRegions() {
const rows = await this.prisma.phoneSegment.findMany({
where: { province: { not: null } },
select: { province: true, city: true },
distinct: ['province', 'city'],
});
const citiesByProvince = new Map<string, Set<string>>();
for (const row of rows) {
const province = row.province?.trim();
if (!province) continue;
const cities = citiesByProvince.get(province) ?? new Set<string>();
const city = row.city?.trim();
if (city) cities.add(city);
citiesByProvince.set(province, cities);
}
return Array.from(citiesByProvince, ([province, cities]) => ({
province,
cities: Array.from(cities).sort((left, right) => left.localeCompare(right, 'zh-CN')),
})).sort((left, right) => left.province.localeCompare(right.province, 'zh-CN'));
}
async listPhoneSegments(query: PhoneSegmentListQuery = {}) {
const page = Math.max(1, Number(query.page ?? 1));
const pageSize = Math.min(100, Math.max(1, Number(query.pageSize ?? 25)));