客服电话 → 13203801799(CUSTOMER_SERVICE_PHONE,协议文案走常量自动同步)
权益页去掉分享按钮,菜单分享保留 支付成功改 reLaunch 清栈;订单列表返回首页 减数量低于起购时 toast「同城/跨城/现场提货至少购买 N 瓶」 核销成功可评星并 POST /redeem/ratings;HQ 新增「门店评价」页 /store-ratings 成功页「回到首页」改用 .redeem-cancel-btn,不再撑破布局 历史记录改为核销流水:GET /redeem/records;生产库已有 2 笔(用户 13073729990)可验证。权益明细字段映射也一并修了 我的页头像提高点击层级,避免被卡片遮挡
This commit is contained in:
@@ -119,7 +119,29 @@ export class BenefitService {
|
||||
where: benefitLedgerWhere(userId, couponId),
|
||||
orderBy: { createdAt: 'desc' },
|
||||
});
|
||||
return serializeBigInt(list);
|
||||
return serializeBigInt(
|
||||
list.map((e) => {
|
||||
const type = e.param1 || '';
|
||||
const amount = Number(e.amount1 ?? 0);
|
||||
const title =
|
||||
e.remark ||
|
||||
(type === 'REDEEM'
|
||||
? '门店核销'
|
||||
: type === 'GRANT'
|
||||
? '购酒入账'
|
||||
: type === 'REFUND_VOID'
|
||||
? '退款作废'
|
||||
: '权益变动');
|
||||
return {
|
||||
id: e.id,
|
||||
type,
|
||||
amount,
|
||||
title,
|
||||
createdAt: e.createdAt,
|
||||
balanceAfter: e.amount2 != null ? Number(e.amount2) : null,
|
||||
};
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
async getCoupon(userId: bigint, couponId: bigint) {
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import { Controller, Get, Query, UseGuards } from '@nestjs/common';
|
||||
import { HqAuthGuard } from '../../common/guards/hq-auth.guard';
|
||||
import { AdminStoreRatingsService } from './admin-store-ratings.service';
|
||||
import { AdminStoreRatingsQueryDto } from './dto/admin-query.dto';
|
||||
|
||||
@Controller('admin/store-ratings')
|
||||
@UseGuards(HqAuthGuard)
|
||||
export class AdminStoreRatingsController {
|
||||
constructor(private readonly service: AdminStoreRatingsService) {}
|
||||
|
||||
@Get()
|
||||
list(@Query() query: AdminStoreRatingsQueryDto) {
|
||||
return this.service.list(query);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Prisma } from '@prisma/client';
|
||||
import { PrismaService } from '../../common/prisma/prisma.module';
|
||||
import { serializeBigInt } from '../../common/decorators/current-user.decorator';
|
||||
import type { AdminStoreRatingsQueryDto } from './dto/admin-query.dto';
|
||||
|
||||
@Injectable()
|
||||
export class AdminStoreRatingsService {
|
||||
constructor(private readonly prisma: PrismaService) {}
|
||||
|
||||
async list(query: AdminStoreRatingsQueryDto) {
|
||||
const page = query.page ?? 1;
|
||||
const pageSize = query.pageSize ?? 20;
|
||||
const where: Prisma.StoreRatingWhereInput = {};
|
||||
if (query.storeId) where.storeId = BigInt(query.storeId);
|
||||
if (query.redeemNo) {
|
||||
where.redeemRecord = { redeemNo: { contains: query.redeemNo } };
|
||||
}
|
||||
|
||||
const [items, total] = await Promise.all([
|
||||
this.prisma.storeRating.findMany({
|
||||
where,
|
||||
orderBy: { createdAt: 'desc' },
|
||||
skip: (page - 1) * pageSize,
|
||||
take: pageSize,
|
||||
include: {
|
||||
store: { select: { id: true, name: true, cityName: true } },
|
||||
redeemRecord: {
|
||||
select: {
|
||||
id: true,
|
||||
redeemNo: true,
|
||||
amount: true,
|
||||
user: { select: { id: true, userNo: true, phone: true, nickname: true } },
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
this.prisma.storeRating.count({ where }),
|
||||
]);
|
||||
|
||||
return serializeBigInt({
|
||||
items: items.map((r) => ({
|
||||
id: r.id,
|
||||
serviceScore: r.serviceScore,
|
||||
envScore: r.envScore,
|
||||
createdAt: r.createdAt,
|
||||
store: r.store,
|
||||
redeemRecordId: r.redeemRecordId,
|
||||
redeemNo: r.redeemRecord.redeemNo,
|
||||
redeemAmount: Number(r.redeemRecord.amount),
|
||||
user: r.redeemRecord.user,
|
||||
})),
|
||||
total,
|
||||
page,
|
||||
pageSize,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -244,6 +244,16 @@ export class AdminRedeemRecordsQueryDto extends PaginationQueryDto {
|
||||
userId?: string;
|
||||
}
|
||||
|
||||
export class AdminStoreRatingsQueryDto extends PaginationQueryDto {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
storeId?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
redeemNo?: string;
|
||||
}
|
||||
|
||||
export class AdminDeliveriesQueryDto extends PaginationQueryDto {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
|
||||
@@ -23,6 +23,8 @@ import { AdminBenefitCouponsController, AdminBenefitLedgersController } from './
|
||||
import { AdminBenefitService } from './admin-benefit.service';
|
||||
import { AdminRedeemRecordsController, AdminDeliveriesController } from './admin-redeem.controller';
|
||||
import { AdminRedeemService, AdminDeliveriesService } from './admin-redeem.service';
|
||||
import { AdminStoreRatingsController } from './admin-store-ratings.controller';
|
||||
import { AdminStoreRatingsService } from './admin-store-ratings.service';
|
||||
import { AdminHqAccountsController } from './admin-hq-accounts.controller';
|
||||
import { AdminHqAccountsService } from './admin-hq-accounts.service';
|
||||
import { AdminProductsController } from './admin-products.controller';
|
||||
@@ -90,6 +92,7 @@ import { AdminFulfillmentProvidersController } from './admin-fulfillment-provide
|
||||
AdminBenefitCouponsController,
|
||||
AdminBenefitLedgersController,
|
||||
AdminRedeemRecordsController,
|
||||
AdminStoreRatingsController,
|
||||
AdminDeliveriesController,
|
||||
AdminHqAccountsController,
|
||||
AdminProductsController,
|
||||
@@ -122,6 +125,7 @@ import { AdminFulfillmentProvidersController } from './admin-fulfillment-provide
|
||||
AdminCitiesService,
|
||||
AdminBenefitService,
|
||||
AdminRedeemService,
|
||||
AdminStoreRatingsService,
|
||||
AdminDeliveriesService,
|
||||
AdminHqAccountsService,
|
||||
AdminProductsService,
|
||||
|
||||
@@ -36,6 +36,19 @@ export class UserRedeemController {
|
||||
rating(@CurrentUser() user: AuthUser, @Body() body: Record<string, unknown>) {
|
||||
return this.redeemService.submitRating(user.actorId, body as never);
|
||||
}
|
||||
|
||||
@Get('records')
|
||||
records(
|
||||
@CurrentUser() user: AuthUser,
|
||||
@Query('page') page = '1',
|
||||
@Query('pageSize') pageSize = '20',
|
||||
) {
|
||||
return this.redeemService.listUserRecords(
|
||||
user.actorId,
|
||||
Number(page),
|
||||
Number(pageSize),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Controller('shop/redeem')
|
||||
|
||||
@@ -1095,11 +1095,51 @@ export class RedeemService {
|
||||
});
|
||||
}
|
||||
|
||||
async listUserRecords(userId: bigint, page = 1, pageSize = 20) {
|
||||
const take = Math.min(Math.max(pageSize, 1), 50);
|
||||
const skip = (Math.max(page, 1) - 1) * take;
|
||||
const [list, total] = await Promise.all([
|
||||
this.prisma.redeemRecord.findMany({
|
||||
where: { userId },
|
||||
orderBy: { createdAt: 'desc' },
|
||||
skip,
|
||||
take,
|
||||
include: {
|
||||
store: { select: { id: true, name: true } },
|
||||
rating: { select: { serviceScore: true, envScore: true } },
|
||||
},
|
||||
}),
|
||||
this.prisma.redeemRecord.count({ where: { userId } }),
|
||||
]);
|
||||
return {
|
||||
list: serializeBigInt(
|
||||
list.map((r) => ({
|
||||
id: r.id,
|
||||
redeemNo: r.redeemNo,
|
||||
amount: Number(r.amount),
|
||||
storeId: r.storeId,
|
||||
storeName: r.store?.name ?? '门店',
|
||||
createdAt: r.createdAt,
|
||||
rating: r.rating
|
||||
? { serviceScore: r.rating.serviceScore, envScore: r.rating.envScore }
|
||||
: null,
|
||||
})),
|
||||
),
|
||||
total,
|
||||
page: Math.max(page, 1),
|
||||
pageSize: take,
|
||||
};
|
||||
}
|
||||
|
||||
async submitRating(userId: bigint, body: { redeemRecordId: string; serviceScore: number; envScore: number }) {
|
||||
const record = await this.prisma.redeemRecord.findFirst({
|
||||
where: { id: BigInt(body.redeemRecordId), userId },
|
||||
});
|
||||
if (!record) throw new NotFoundException('核销记录不存在');
|
||||
const existing = await this.prisma.storeRating.findUnique({
|
||||
where: { redeemRecordId: record.id },
|
||||
});
|
||||
if (existing) return serializeBigInt(existing);
|
||||
const rating = await this.prisma.storeRating.create({
|
||||
data: {
|
||||
redeemRecordId: record.id,
|
||||
|
||||
Reference in New Issue
Block a user