feat(analytics): persona logging upgrade and Sentry system config
Add client-logging SDK, expanded event taxonomy, API observability, admin domain events UI, and move SENTRY_DSN to HQ system settings with @sentry/node bootstrap after config preload. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
import { Controller, Get, Param, Query, UseGuards } from '@nestjs/common';
|
||||
import type { EventType } from '@prisma/client';
|
||||
import { HqAuthGuard } from '../../common/guards/hq-auth.guard';
|
||||
import { AdminDomainEventsService } from './admin-domain-events.service';
|
||||
|
||||
@Controller('admin/logs/domain-events')
|
||||
@UseGuards(HqAuthGuard)
|
||||
export class AdminDomainEventsController {
|
||||
constructor(private readonly service: AdminDomainEventsService) {}
|
||||
|
||||
@Get()
|
||||
list(
|
||||
@Query('page') page?: string,
|
||||
@Query('pageSize') pageSize?: string,
|
||||
@Query('eventType') eventType?: EventType,
|
||||
@Query('refType') refType?: string,
|
||||
@Query('refId') refId?: string,
|
||||
@Query('from') from?: string,
|
||||
@Query('to') to?: string,
|
||||
) {
|
||||
return this.service.list({
|
||||
page: page ? Number(page) : undefined,
|
||||
pageSize: pageSize ? Number(pageSize) : undefined,
|
||||
eventType,
|
||||
refType,
|
||||
refId,
|
||||
from,
|
||||
to,
|
||||
});
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
detail(@Param('id') id: string) {
|
||||
return this.service.detail(BigInt(id));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
import type { EventType, Prisma } from '@prisma/client';
|
||||
import { PrismaService } from '../../common/prisma/prisma.module';
|
||||
import { serializeBigInt } from '../../common/decorators/current-user.decorator';
|
||||
|
||||
const DOMAIN_EVENT_TYPES: EventType[] = [
|
||||
'ORDER_STATUS',
|
||||
'BENEFIT_LEDGER',
|
||||
'STORE_AUDIT',
|
||||
'TICKET_COLLAB',
|
||||
'PROMO_TOUCH',
|
||||
];
|
||||
|
||||
@Injectable()
|
||||
export class AdminDomainEventsService {
|
||||
constructor(private readonly prisma: PrismaService) {}
|
||||
|
||||
async list(query: {
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
eventType?: EventType;
|
||||
refType?: string;
|
||||
refId?: string;
|
||||
from?: string;
|
||||
to?: string;
|
||||
}) {
|
||||
const page = query.page ?? 1;
|
||||
const pageSize = query.pageSize ?? 20;
|
||||
const where: Prisma.CommonEventWhereInput = {
|
||||
eventType: query.eventType ?? { in: DOMAIN_EVENT_TYPES },
|
||||
};
|
||||
if (query.refType) where.refType = query.refType;
|
||||
if (query.refId) where.refId = BigInt(query.refId);
|
||||
if (query.from || query.to) {
|
||||
where.createdAt = {};
|
||||
if (query.from) where.createdAt.gte = new Date(query.from);
|
||||
if (query.to) where.createdAt.lte = new Date(query.to);
|
||||
}
|
||||
|
||||
const [items, total] = await Promise.all([
|
||||
this.prisma.commonEvent.findMany({
|
||||
where,
|
||||
orderBy: { createdAt: 'desc' },
|
||||
skip: (page - 1) * pageSize,
|
||||
take: pageSize,
|
||||
}),
|
||||
this.prisma.commonEvent.count({ where }),
|
||||
]);
|
||||
return serializeBigInt({ items, total, page, pageSize });
|
||||
}
|
||||
|
||||
async detail(id: bigint) {
|
||||
const row = await this.prisma.commonEvent.findUnique({ where: { id } });
|
||||
if (!row || !DOMAIN_EVENT_TYPES.includes(row.eventType)) {
|
||||
throw new NotFoundException('领域事件不存在');
|
||||
}
|
||||
return serializeBigInt(row);
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import { mapStoreCompat } from '../../common/compat/v31-compat';
|
||||
import type { AdminStoreAccountsQueryDto, AdminStoreMediaQueryDto, AdminStoresQueryDto } from './dto/admin-query.dto';
|
||||
import { PartnerCityService } from '../city-scope/partner-city.service';
|
||||
import { StoreCategoryService } from '../store/store-category.service';
|
||||
import { AnalyticsService } from '../analytics/analytics.service';
|
||||
import type {
|
||||
CreateStoreAccountDto,
|
||||
CreateStoreDto,
|
||||
@@ -49,6 +50,7 @@ export class AdminStoresService {
|
||||
private readonly prisma: PrismaService,
|
||||
private readonly partnerCityService: PartnerCityService,
|
||||
private readonly storeCategoryService: StoreCategoryService,
|
||||
private readonly analyticsService: AnalyticsService,
|
||||
) {}
|
||||
|
||||
async listStores(query: AdminStoresQueryDto) {
|
||||
@@ -205,6 +207,19 @@ export class AdminStoresService {
|
||||
},
|
||||
});
|
||||
|
||||
if (updated.partnerAccountId) {
|
||||
this.analyticsService.trackPartnerOneSafe(undefined, 'HQ_WEB', {
|
||||
partnerAccountId: updated.partnerAccountId,
|
||||
eventName: dto.approved ? 'partner_store_audit_approved' : 'partner_store_audit_rejected',
|
||||
refType: 'STORE',
|
||||
refId: id,
|
||||
extraJson: {
|
||||
storeId: id.toString(),
|
||||
remark: dto.remark?.trim() || null,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return serializeBigInt({
|
||||
...updated,
|
||||
notifyHint: dto.approved
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Module } from '@nestjs/common';
|
||||
import { CityScopeModule } from '../city-scope/city-scope.module';
|
||||
import { IamModule } from '../iam/iam.module';
|
||||
import { TradeModule } from '../trade/trade.module';
|
||||
import { AnalyticsModule } from '../analytics/analytics.module';
|
||||
import { FulfillmentModule } from '../fulfillment/fulfillment.module';
|
||||
import { StoreModule } from '../store/store.module';
|
||||
import { AdminDashboardController } from './admin-dashboard.controller';
|
||||
@@ -71,9 +72,11 @@ import { AdminLlmConfigsService } from './admin-llm-configs.service';
|
||||
import { AdminKnowledgeBasesController } from './admin-knowledge-bases.controller';
|
||||
import { AdminKnowledgeBasesService } from './admin-knowledge-bases.service';
|
||||
import { AdminFulfillmentProvidersController } from './admin-fulfillment-providers.controller';
|
||||
import { AdminDomainEventsController } from './admin-domain-events.controller';
|
||||
import { AdminDomainEventsService } from './admin-domain-events.service';
|
||||
|
||||
@Module({
|
||||
imports: [CityScopeModule, IamModule, TradeModule, FulfillmentModule, BenefitModule, CommonModule, IntegrationsModule, WecomModule, LlmModule, RedeemModule, StoreModule],
|
||||
imports: [CityScopeModule, IamModule, TradeModule, AnalyticsModule, FulfillmentModule, BenefitModule, CommonModule, IntegrationsModule, WecomModule, LlmModule, RedeemModule, StoreModule],
|
||||
controllers: [
|
||||
AdminDashboardController,
|
||||
AdminDeployController,
|
||||
@@ -100,6 +103,7 @@ import { AdminFulfillmentProvidersController } from './admin-fulfillment-provide
|
||||
AdminStoreLogsController,
|
||||
AdminPartnerLogsController,
|
||||
AdminHqLogsController,
|
||||
AdminDomainEventsController,
|
||||
AdminOssLogsController,
|
||||
AdminTicketsController,
|
||||
AdminSupportTicketsController,
|
||||
@@ -133,6 +137,7 @@ import { AdminFulfillmentProvidersController } from './admin-fulfillment-provide
|
||||
AdminStoreLogsService,
|
||||
AdminPartnerLogsService,
|
||||
AdminHqLogsService,
|
||||
AdminDomainEventsService,
|
||||
AdminOssLogsService,
|
||||
AdminTicketsService,
|
||||
AdminXiaofeixiaService,
|
||||
|
||||
Reference in New Issue
Block a user