城市合伙人H5端结算审核
This commit is contained in:
@@ -183,6 +183,7 @@ enum PartnerBillStatus {
|
||||
DRAFT
|
||||
CONFIRMED
|
||||
PAID
|
||||
REJECTED
|
||||
}
|
||||
|
||||
enum StoreStatus {
|
||||
@@ -623,6 +624,7 @@ model PartnerBill {
|
||||
status PartnerBillStatus @default(DRAFT)
|
||||
confirmedAt DateTime? @map("confirmed_at") @db.DateTime(3)
|
||||
paidAt DateTime? @map("paid_at") @db.DateTime(3)
|
||||
rejectReason String? @map("reject_reason") @db.VarChar(500)
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3)
|
||||
|
||||
partnerAccount PartnerAccount @relation(fields: [partnerAccountId], references: [id], onDelete: Restrict)
|
||||
|
||||
@@ -46,6 +46,7 @@ export const HqOperationAction = {
|
||||
PARTNER_BILL_GENERATE: 'PARTNER_BILL_GENERATE',
|
||||
PARTNER_BILL_CONFIRM: 'PARTNER_BILL_CONFIRM',
|
||||
PARTNER_BILL_MARK_PAID: 'PARTNER_BILL_MARK_PAID',
|
||||
PARTNER_BILL_REJECT: 'PARTNER_BILL_REJECT',
|
||||
REDEEM_DEBUG_CREATE_TOKEN: 'REDEEM_DEBUG_CREATE_TOKEN',
|
||||
REDEEM_DEBUG_CONFIRM: 'REDEEM_DEBUG_CONFIRM',
|
||||
PROMO_CODE_CREATE: 'PROMO_CODE_CREATE',
|
||||
@@ -108,6 +109,7 @@ export const HQ_OPERATION_ACTION_LABELS: Record<string, string> = {
|
||||
[HqOperationAction.PARTNER_BILL_GENERATE]: '生成合伙人账单',
|
||||
[HqOperationAction.PARTNER_BILL_CONFIRM]: '确认合伙人账单',
|
||||
[HqOperationAction.PARTNER_BILL_MARK_PAID]: '合伙人账单结算',
|
||||
[HqOperationAction.PARTNER_BILL_REJECT]: '驳回合伙人打款申请',
|
||||
[HqOperationAction.REDEEM_DEBUG_CREATE_TOKEN]: '核销调试-生成码',
|
||||
[HqOperationAction.REDEEM_DEBUG_CONFIRM]: '核销调试-确认核销',
|
||||
[HqOperationAction.PROMO_CODE_CREATE]: '创建推广码',
|
||||
|
||||
@@ -22,6 +22,7 @@ export class AdminDashboardService {
|
||||
deliveriesTotal,
|
||||
pendingPayouts,
|
||||
pendingBills,
|
||||
pendingPartnerDraftBills,
|
||||
openTickets,
|
||||
] = await Promise.all([
|
||||
this.prisma.user.count({ where: { status: 1, mergedIntoUserId: null } }),
|
||||
@@ -42,7 +43,8 @@ export class AdminDashboardService {
|
||||
this.prisma.redeemRecord.count({ where: { createdAt: { gte: todayStart } } }),
|
||||
this.prisma.orderDelivery.count(),
|
||||
this.prisma.storePayout.count({ where: { status: 'PENDING' } }),
|
||||
this.prisma.partnerBill.count({ where: { status: { in: ['DRAFT', 'CONFIRMED'] } } }),
|
||||
this.prisma.partnerBill.count({ where: { status: 'CONFIRMED' } }),
|
||||
this.prisma.partnerBill.count({ where: { status: 'DRAFT' } }),
|
||||
this.prisma.commonTicket.count({ where: { status: { in: ['PENDING', 'OPEN'] } } }),
|
||||
]);
|
||||
|
||||
@@ -57,7 +59,9 @@ export class AdminDashboardService {
|
||||
redeemToday,
|
||||
deliveriesTotal,
|
||||
pendingPayouts,
|
||||
/** 合伙人已确认、待总部审核打款 */
|
||||
pendingBills,
|
||||
pendingPartnerDraftBills,
|
||||
openTickets,
|
||||
ordersByStatus: ordersByStatus.map((row) => ({
|
||||
status: row.status,
|
||||
|
||||
@@ -25,6 +25,11 @@ export class SettlementController {
|
||||
bills(@CurrentUser() user: AuthUser) {
|
||||
return this.settlementService.listPartnerBills(user.actorId);
|
||||
}
|
||||
|
||||
@Post('bills/:id/confirm')
|
||||
confirm(@CurrentUser() user: AuthUser, @Param('id') id: string) {
|
||||
return this.settlementService.partnerConfirmBill(user.actorId, BigInt(id));
|
||||
}
|
||||
}
|
||||
|
||||
@Controller('shop/payouts')
|
||||
@@ -193,6 +198,20 @@ export class AdminPartnerBillController {
|
||||
markPaid(@Param('id') id: string, @Body() body: { paymentRef?: string }) {
|
||||
return this.settlementService.markPartnerBillPaid(BigInt(id), body);
|
||||
}
|
||||
|
||||
@Post(':id/reject')
|
||||
@HqOperation({
|
||||
action: HqOperationAction.PARTNER_BILL_REJECT,
|
||||
refType: 'PARTNER_BILL',
|
||||
refIdParam: 'id',
|
||||
includeBody: true,
|
||||
})
|
||||
reject(@Param('id') id: string, @Body() body: { reason?: string }) {
|
||||
if (!body?.reason?.trim()) {
|
||||
throw new BadRequestException('请填写驳回理由');
|
||||
}
|
||||
return this.settlementService.rejectPartnerBill(BigInt(id), body.reason);
|
||||
}
|
||||
}
|
||||
|
||||
@Controller('admin/winery-bills')
|
||||
|
||||
@@ -529,11 +529,70 @@ export class SettlementService {
|
||||
async confirmPartnerBill(id: bigint) {
|
||||
const bill = await this.prisma.partnerBill.findUnique({ where: { id } });
|
||||
if (!bill) throw new NotFoundException('账单不存在');
|
||||
if (bill.status !== 'DRAFT') throw new BadRequestException('仅草稿可确认');
|
||||
if (bill.status !== 'DRAFT' && bill.status !== 'REJECTED') {
|
||||
throw new BadRequestException('仅待确认或已驳回账单可确认');
|
||||
}
|
||||
|
||||
const updated = await this.prisma.partnerBill.update({
|
||||
where: { id },
|
||||
data: { status: 'CONFIRMED', confirmedAt: new Date() },
|
||||
data: {
|
||||
status: 'CONFIRMED',
|
||||
confirmedAt: new Date(),
|
||||
rejectReason: null,
|
||||
},
|
||||
});
|
||||
|
||||
return serializeBigInt(updated);
|
||||
}
|
||||
|
||||
/** 合伙人端:确认账单并申请打款(DRAFT/REJECTED → CONFIRMED) */
|
||||
async partnerConfirmBill(partnerAccountId: bigint, billId: bigint) {
|
||||
const primary = await this.partnerCityService.resolvePrimaryAccount(partnerAccountId);
|
||||
const bill = await this.prisma.partnerBill.findUnique({ where: { id: billId } });
|
||||
if (!bill || bill.partnerAccountId !== primary.id) {
|
||||
throw new NotFoundException('账单不存在');
|
||||
}
|
||||
if (bill.status !== 'DRAFT' && bill.status !== 'REJECTED') {
|
||||
throw new BadRequestException('当前账单不可再次申请打款');
|
||||
}
|
||||
|
||||
const updated = await this.prisma.partnerBill.update({
|
||||
where: { id: billId },
|
||||
data: {
|
||||
status: 'CONFIRMED',
|
||||
confirmedAt: new Date(),
|
||||
rejectReason: null,
|
||||
},
|
||||
});
|
||||
|
||||
this.analyticsService.trackPartnerOneSafe(partnerAccountId, 'PARTNER_H5', {
|
||||
partnerAccountId: primary.id,
|
||||
eventName: 'partner_bill_confirm',
|
||||
refType: 'PARTNER_BILL',
|
||||
refId: billId,
|
||||
extraJson: { billNo: bill.billNo },
|
||||
});
|
||||
|
||||
return serializeBigInt(updated);
|
||||
}
|
||||
|
||||
async rejectPartnerBill(id: bigint, reason: string) {
|
||||
const rejectReason = reason.trim();
|
||||
if (!rejectReason) throw new BadRequestException('请填写驳回理由');
|
||||
if (rejectReason.length > 500) throw new BadRequestException('驳回理由不能超过 500 字');
|
||||
|
||||
const bill = await this.prisma.partnerBill.findUnique({ where: { id } });
|
||||
if (!bill) throw new NotFoundException('账单不存在');
|
||||
if (bill.status !== 'CONFIRMED') {
|
||||
throw new BadRequestException('仅待打款审核的账单可驳回');
|
||||
}
|
||||
|
||||
const updated = await this.prisma.partnerBill.update({
|
||||
where: { id },
|
||||
data: {
|
||||
status: 'REJECTED',
|
||||
rejectReason,
|
||||
},
|
||||
});
|
||||
|
||||
return serializeBigInt(updated);
|
||||
@@ -542,11 +601,11 @@ export class SettlementService {
|
||||
async markPartnerBillPaid(id: bigint, dto: { paymentRef?: string }) {
|
||||
const bill = await this.prisma.partnerBill.findUnique({ where: { id } });
|
||||
if (!bill) throw new NotFoundException('账单不存在');
|
||||
if (bill.status !== 'CONFIRMED') throw new BadRequestException('仅已确认账单可标记打款');
|
||||
if (bill.status !== 'CONFIRMED') throw new BadRequestException('仅待打款审核的账单可标记打款');
|
||||
|
||||
const updated = await this.prisma.partnerBill.update({
|
||||
where: { id },
|
||||
data: { status: 'PAID', paidAt: new Date() },
|
||||
data: { status: 'PAID', paidAt: new Date(), rejectReason: null },
|
||||
});
|
||||
|
||||
return serializeBigInt(updated);
|
||||
@@ -577,6 +636,7 @@ export class SettlementService {
|
||||
'状态',
|
||||
'确认时间',
|
||||
'打款时间',
|
||||
'驳回理由',
|
||||
].join(',');
|
||||
const rows = bills.map((b) =>
|
||||
[
|
||||
@@ -591,6 +651,7 @@ export class SettlementService {
|
||||
b.status,
|
||||
b.confirmedAt ? b.confirmedAt.toISOString().slice(0, 10) : '',
|
||||
b.paidAt ? b.paidAt.toISOString().slice(0, 10) : '',
|
||||
csvEscape(b.rejectReason ?? ''),
|
||||
].join(','),
|
||||
);
|
||||
return { csv: `\uFEFF${[header, ...rows].join('\n')}`, count: bills.length };
|
||||
|
||||
Reference in New Issue
Block a user