城市合伙人端的修改(后台)
This commit is contained in:
@@ -11,6 +11,7 @@ import { serializeBigInt } from '../../common/decorators/current-user.decorator'
|
||||
import { mapStoreCompat } from '../../common/compat/v31-compat';
|
||||
import { parseBigIntParam } from '../../common/parse-bigint';
|
||||
import { AnalyticsService } from '../analytics/analytics.service';
|
||||
import { PartnerCityService } from '../city-scope/partner-city.service';
|
||||
|
||||
@Injectable()
|
||||
export class StoreService {
|
||||
@@ -19,6 +20,7 @@ export class StoreService {
|
||||
constructor(
|
||||
private readonly prisma: PrismaService,
|
||||
private readonly analyticsService: AnalyticsService,
|
||||
private readonly partnerCityService: PartnerCityService,
|
||||
) {}
|
||||
|
||||
async listOpenStores(cityCode?: string) {
|
||||
@@ -48,9 +50,15 @@ export class StoreService {
|
||||
return serializeBigInt(mapStoreCompat({ ...store, media }));
|
||||
}
|
||||
|
||||
private async resolvePartnerScope(actorAccountId: bigint) {
|
||||
const account = await this.getPartnerAccount(actorAccountId);
|
||||
const primaryId = await this.getPartnerPrimaryId(actorAccountId);
|
||||
return { account, primaryId };
|
||||
}
|
||||
|
||||
async partnerListStores(partnerAccountId: bigint) {
|
||||
const account = await this.getPartnerAccount(partnerAccountId);
|
||||
const where: { partnerId: bigint; id?: { in: bigint[] } } = { partnerId: account.partnerId };
|
||||
const { account, primaryId } = await this.resolvePartnerScope(partnerAccountId);
|
||||
const where: { partnerAccountId: bigint; id?: { in: bigint[] } } = { partnerAccountId: primaryId };
|
||||
if (this.isSubAccount(account)) {
|
||||
const storeIds = await this.getStoreIdsCreatedByAccount(partnerAccountId);
|
||||
if (storeIds.length === 0) return [];
|
||||
@@ -65,9 +73,9 @@ export class StoreService {
|
||||
}
|
||||
|
||||
async partnerGetStore(partnerAccountId: bigint, storeId: bigint) {
|
||||
const account = await this.getPartnerAccount(partnerAccountId);
|
||||
const { account, primaryId } = await this.resolvePartnerScope(partnerAccountId);
|
||||
const store = await this.prisma.store.findFirst({
|
||||
where: { id: storeId, partnerId: account.partnerId },
|
||||
where: { id: storeId, partnerAccountId: primaryId },
|
||||
include: { category: true, coverResource: true },
|
||||
});
|
||||
if (!store) throw new NotFoundException('门店不存在');
|
||||
@@ -88,10 +96,11 @@ export class StoreService {
|
||||
}
|
||||
|
||||
async partnerListCities(partnerAccountId: bigint) {
|
||||
const account = await this.getPartnerAccount(partnerAccountId);
|
||||
const { primaryId } = await this.resolvePartnerScope(partnerAccountId);
|
||||
const cityWhere = await this.partnerCityService.buildPartnerCityWhere(primaryId);
|
||||
const cities = await this.prisma.commonCity.findMany({
|
||||
where: { partnerId: account.partnerId },
|
||||
select: { id: true, name: true, code: true, province: true, partnerId: true },
|
||||
where: cityWhere,
|
||||
select: { id: true, name: true, code: true, province: true },
|
||||
orderBy: { createdAt: 'desc' },
|
||||
});
|
||||
return serializeBigInt(cities);
|
||||
@@ -115,11 +124,11 @@ export class StoreService {
|
||||
}
|
||||
|
||||
async createStore(partnerAccountId: bigint, body: Record<string, unknown>) {
|
||||
const account = await this.getPartnerAccount(partnerAccountId);
|
||||
const { primaryId } = await this.resolvePartnerScope(partnerAccountId);
|
||||
const normalizedPhone = String(body.phone).trim();
|
||||
await this.assertStorePhoneAvailable(normalizedPhone);
|
||||
|
||||
const city = await this.resolvePartnerCity(account.partnerId, body.cityId);
|
||||
const city = await this.resolvePartnerCity(partnerAccountId, body.cityId);
|
||||
const coverUrl = body.coverUrl ? String(body.coverUrl).trim() : '';
|
||||
const envPhotoUrls = Array.isArray(body.envPhotoUrls)
|
||||
? body.envPhotoUrls.map((u) => String(u).trim()).filter(Boolean)
|
||||
@@ -133,7 +142,7 @@ export class StoreService {
|
||||
const store = await this.prisma.store.create({
|
||||
data: {
|
||||
cityId: city.id,
|
||||
partnerId: account.partnerId,
|
||||
partnerAccountId: primaryId,
|
||||
categoryId: body.categoryId ? parseBigIntParam(body.categoryId, '分类ID') : null,
|
||||
name: String(body.name),
|
||||
phone: normalizedPhone,
|
||||
@@ -220,7 +229,7 @@ export class StoreService {
|
||||
});
|
||||
|
||||
this.analyticsService.trackPartnerOneSafe(partnerAccountId, 'PARTNER_H5', {
|
||||
partnerId: account.partnerId,
|
||||
partnerAccountId: primaryId,
|
||||
eventName: 'partner_store_create',
|
||||
refType: 'STORE',
|
||||
refId: store.id,
|
||||
@@ -240,10 +249,10 @@ export class StoreService {
|
||||
storeId: bigint,
|
||||
status: 'OPEN' | 'PAUSED' | 'CLOSED',
|
||||
) {
|
||||
const account = await this.getPartnerAccount(partnerAccountId);
|
||||
const { account, primaryId } = await this.resolvePartnerScope(partnerAccountId);
|
||||
this.assertPrimaryAccount(account);
|
||||
const store = await this.prisma.store.findFirst({
|
||||
where: { id: storeId, partnerId: account.partnerId },
|
||||
where: { id: storeId, partnerAccountId: primaryId },
|
||||
});
|
||||
if (!store) throw new NotFoundException('门店不存在');
|
||||
if (store.status === 'CLOSED') {
|
||||
@@ -259,7 +268,7 @@ export class StoreService {
|
||||
include: { coverResource: true },
|
||||
});
|
||||
this.analyticsService.trackPartnerOneSafe(partnerAccountId, 'PARTNER_H5', {
|
||||
partnerId: account.partnerId,
|
||||
partnerAccountId: primaryId,
|
||||
eventName: 'partner_store_status_change',
|
||||
refType: 'STORE',
|
||||
refId: storeId,
|
||||
@@ -276,10 +285,10 @@ export class StoreService {
|
||||
storeId: bigint,
|
||||
body: Record<string, unknown>,
|
||||
) {
|
||||
const account = await this.getPartnerAccount(partnerAccountId);
|
||||
const { account, primaryId } = await this.resolvePartnerScope(partnerAccountId);
|
||||
this.assertPrimaryAccount(account);
|
||||
const store = await this.prisma.store.findFirst({
|
||||
where: { id: storeId, partnerId: account.partnerId },
|
||||
where: { id: storeId, partnerAccountId: primaryId },
|
||||
});
|
||||
if (!store) throw new NotFoundException('门店不存在');
|
||||
if (store.status === 'CLOSED') {
|
||||
@@ -343,20 +352,20 @@ export class StoreService {
|
||||
}
|
||||
|
||||
async partnerDashboard(partnerAccountId: bigint) {
|
||||
const account = await this.getPartnerAccount(partnerAccountId);
|
||||
const { account, primaryId } = await this.resolvePartnerScope(partnerAccountId);
|
||||
this.assertPrimaryAccount(account);
|
||||
const partnerStoreIds = await this.prisma.store.findMany({
|
||||
where: { partnerId: account.partnerId },
|
||||
where: { partnerAccountId: primaryId },
|
||||
select: { id: true },
|
||||
});
|
||||
const storeIds = partnerStoreIds.map((s) => s.id);
|
||||
const [storeCount, orderCount, recentStores, pendingAuditCount] = await Promise.all([
|
||||
this.prisma.store.count({ where: { partnerId: account.partnerId } }),
|
||||
this.prisma.store.count({ where: { partnerAccountId: primaryId } }),
|
||||
this.prisma.order.count({
|
||||
where: { city: { partnerId: account.partnerId } },
|
||||
where: await this.partnerCityService.buildPartnerOrderWhere(primaryId),
|
||||
}),
|
||||
this.prisma.store.findMany({
|
||||
where: { partnerId: account.partnerId },
|
||||
where: { partnerAccountId: primaryId },
|
||||
select: { id: true, name: true, status: true, createdAt: true },
|
||||
orderBy: { createdAt: 'desc' },
|
||||
take: 10,
|
||||
@@ -375,18 +384,17 @@ export class StoreService {
|
||||
return {
|
||||
storeCount,
|
||||
orderCount,
|
||||
companyName: account.partner.companyName,
|
||||
companyName: account.companyName ?? '',
|
||||
recentStores: serializeBigInt(recentStores),
|
||||
pendingAuditCount,
|
||||
};
|
||||
}
|
||||
|
||||
async partnerLeaderboard(partnerAccountId: bigint, period: PartnerLeaderboardPeriod = 'total') {
|
||||
const account = await this.getPartnerAccount(partnerAccountId);
|
||||
this.assertPrimaryAccount(account);
|
||||
const { primaryId } = await this.resolvePartnerScope(partnerAccountId);
|
||||
|
||||
const accounts = await this.prisma.partnerAccount.findMany({
|
||||
where: { partnerId: account.partnerId },
|
||||
where: { OR: [{ id: primaryId }, { parentAccountId: primaryId }] },
|
||||
orderBy: { id: 'asc' },
|
||||
});
|
||||
|
||||
@@ -457,10 +465,9 @@ export class StoreService {
|
||||
return { period, list, self };
|
||||
}
|
||||
|
||||
async partnerWeeklyReport(partnerAccountId: bigint, startDate?: string) {
|
||||
const account = await this.getPartnerAccount(partnerAccountId);
|
||||
async partnerWeeklyReport(actorAccountId: bigint, startDate?: string) {
|
||||
const { account, primaryId } = await this.resolvePartnerScope(actorAccountId);
|
||||
this.assertPrimaryAccount(account);
|
||||
const partnerId = account.partnerId;
|
||||
|
||||
const currentWeekStart = this.startOfWeekMonday(new Date());
|
||||
const periodStart =
|
||||
@@ -472,16 +479,16 @@ export class StoreService {
|
||||
const prevPeriodEnd = periodStart;
|
||||
|
||||
const newStoreTarget =
|
||||
account.partner.weeklyStoreTarget ??
|
||||
account.weeklyStoreTarget ??
|
||||
Number(process.env.PARTNER_WEEKLY_STORE_TARGET ?? 20);
|
||||
|
||||
const orderWhere = {
|
||||
city: { partnerId },
|
||||
...(await this.partnerCityService.buildPartnerOrderWhere(primaryId)),
|
||||
payStatus: 'PAID' as const,
|
||||
paidAt: { gte: periodStart, lt: periodEnd },
|
||||
};
|
||||
const prevOrderWhere = {
|
||||
city: { partnerId },
|
||||
...(await this.partnerCityService.buildPartnerOrderWhere(primaryId)),
|
||||
payStatus: 'PAID' as const,
|
||||
paidAt: { gte: prevPeriodStart, lt: prevPeriodEnd },
|
||||
};
|
||||
@@ -498,16 +505,16 @@ export class StoreService {
|
||||
] = await Promise.all([
|
||||
this.prisma.order.aggregate({ where: orderWhere, _sum: { payAmount: true } }),
|
||||
this.prisma.order.count({ where: orderWhere }),
|
||||
this.prisma.store.count({ where: { partnerId } }),
|
||||
this.prisma.store.count({ where: { partnerAccountId: primaryId } }),
|
||||
this.prisma.store.count({
|
||||
where: { partnerId, createdAt: { gte: periodStart, lt: periodEnd } },
|
||||
where: { partnerAccountId: primaryId, createdAt: { gte: periodStart, lt: periodEnd } },
|
||||
}),
|
||||
this.prisma.order.aggregate({ where: prevOrderWhere, _sum: { payAmount: true } }),
|
||||
this.prisma.redeemRecord.groupBy({
|
||||
by: ['storeId'],
|
||||
where: {
|
||||
createdAt: { gte: periodStart, lt: periodEnd },
|
||||
store: { partnerId },
|
||||
store: { partnerAccountId: primaryId },
|
||||
},
|
||||
_sum: { amount: true },
|
||||
orderBy: { _sum: { amount: 'desc' } },
|
||||
@@ -516,7 +523,7 @@ export class StoreService {
|
||||
this.prisma.redeemRecord.findMany({
|
||||
where: {
|
||||
createdAt: { gte: periodStart, lt: periodEnd },
|
||||
store: { partnerId },
|
||||
store: { partnerAccountId: primaryId },
|
||||
},
|
||||
select: { storeId: true },
|
||||
distinct: ['storeId'],
|
||||
@@ -677,22 +684,29 @@ export class StoreService {
|
||||
return ['周一', '周二', '周三', '周四', '周五', '周六', '周日'][index] ?? '';
|
||||
}
|
||||
|
||||
private async getPartnerPrimaryId(actorAccountId: bigint) {
|
||||
const primary = await this.partnerCityService.resolvePrimaryAccount(actorAccountId);
|
||||
return primary.id;
|
||||
}
|
||||
|
||||
private async getPartnerAccount(partnerAccountId: bigint) {
|
||||
return this.prisma.partnerAccount.findUniqueOrThrow({
|
||||
where: { id: partnerAccountId },
|
||||
include: { partner: true },
|
||||
});
|
||||
}
|
||||
|
||||
private async resolvePartnerCity(partnerId: bigint, cityId: unknown) {
|
||||
private async resolvePartnerCity(actorAccountId: bigint, cityId: unknown) {
|
||||
const primaryId = await this.getPartnerPrimaryId(actorAccountId);
|
||||
if (cityId) {
|
||||
const city = await this.prisma.commonCity.findFirst({
|
||||
where: { id: parseBigIntParam(cityId, '城市ID'), partnerId },
|
||||
});
|
||||
const id = parseBigIntParam(cityId, '城市ID');
|
||||
await this.partnerCityService.assertPartnerAccountBoundToCity(primaryId, id);
|
||||
const city = await this.prisma.commonCity.findUnique({ where: { id } });
|
||||
if (!city) throw new BadRequestException('所选地区未匹配到开城城市');
|
||||
return city;
|
||||
}
|
||||
const city = await this.prisma.commonCity.findFirst({ where: { partnerId } });
|
||||
const cityIds = await this.partnerCityService.listCityIdsForPartnerAccount(primaryId);
|
||||
if (!cityIds.length) throw new BadRequestException('合伙人未绑定开城');
|
||||
const city = await this.prisma.commonCity.findFirst({ where: { id: { in: cityIds } } });
|
||||
if (!city) throw new BadRequestException('合伙人未绑定开城');
|
||||
return city;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user