feat(store): 门店详情核销记录走马灯

新增 GET /stores/:id/recent-redeems,C 端在门店详情区上方循环展示脱敏核销动态。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-03 14:16:06 +08:00
parent e06d9f4711
commit 4a5b9eaffa
4 changed files with 144 additions and 1 deletions
@@ -38,6 +38,15 @@ type TokenPayload = {
allocations?: Array<{ couponId: string; amount: number }>;
};
/** C 端公示:用户138****5678 / 用户*** */
function maskRedeemUserLabel(phone?: string | null): string {
const digits = String(phone || '').replace(/\D/g, '');
if (digits.length >= 7) {
return `用户${digits.slice(0, 3)}****${digits.slice(-4)}`;
}
return '用户***';
}
type PendingSnapshot = TokenPayload & {
redeemType: 'DIRECT' | 'COUPON';
};
@@ -1131,6 +1140,22 @@ export class RedeemService {
};
}
/** C 端门店详情走马灯:脱敏用户 + 时间 + 金额 */
async listPublicStoreRecentRedeems(storeId: bigint, limit = 20) {
const take = Math.min(Math.max(limit, 1), 50);
const list = await this.prisma.redeemRecord.findMany({
where: { storeId },
orderBy: { createdAt: 'desc' },
take,
include: { user: { select: { phone: true } } },
});
return list.map((r) => ({
userLabel: maskRedeemUserLabel(r.user?.phone),
amount: Number(r.amount),
createdAt: r.createdAt.toISOString(),
}));
}
async submitRating(userId: bigint, body: { redeemRecordId: string; serviceScore: number; envScore: number }) {
const record = await this.prisma.redeemRecord.findFirst({
where: { id: BigInt(body.redeemRecordId), userId },