核销功能
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import {
|
import {
|
||||||
Alert, Button, Card, Col, Form, Input, InputNumber, Row, Space, Tabs, Typography, message,
|
Button, Card, Col, Form, Input, InputNumber, Row, Space, Tabs, Typography, message,
|
||||||
} from 'antd';
|
} from 'antd';
|
||||||
import { request } from '../lib/api';
|
import { request } from '../lib/api';
|
||||||
|
|
||||||
@@ -56,13 +56,6 @@ export default function RedeemDebugPage() {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Typography.Title level={4}>核销调试</Typography.Title>
|
<Typography.Title level={4}>核销调试</Typography.Title>
|
||||||
<Alert
|
|
||||||
type="warning"
|
|
||||||
showIcon
|
|
||||||
style={{ marginBottom: 16 }}
|
|
||||||
message="仅用于 preV1 联调"
|
|
||||||
description="模拟 C 端生成核销码、门店预览与确认核销。确认核销会真实扣减权益并写入核销记录。"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Tabs
|
<Tabs
|
||||||
items={[
|
items={[
|
||||||
@@ -74,8 +67,12 @@ export default function RedeemDebugPage() {
|
|||||||
<Col xs={24} lg={10}>
|
<Col xs={24} lg={10}>
|
||||||
<Card size="small" title="参数">
|
<Card size="small" title="参数">
|
||||||
<Form form={createForm} layout="vertical">
|
<Form form={createForm} layout="vertical">
|
||||||
<Form.Item name="userId" label="用户 ID" rules={[{ required: true }]}>
|
<Form.Item
|
||||||
<Input placeholder="用户表 id" />
|
name="userId"
|
||||||
|
label="用户"
|
||||||
|
rules={[{ required: true, message: '请填写用户 ID、编号或手机号' }]}
|
||||||
|
>
|
||||||
|
<Input placeholder="数据库 ID / 用户编号 / 手机号" />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item name="amount" label="核销金额" rules={[{ required: true }]}>
|
<Form.Item name="amount" label="核销金额" rules={[{ required: true }]}>
|
||||||
<InputNumber min={0.01} max={500} step={1} style={{ width: '100%' }} />
|
<InputNumber min={0.01} max={500} step={1} style={{ width: '100%' }} />
|
||||||
@@ -90,8 +87,9 @@ export default function RedeemDebugPage() {
|
|||||||
type="primary"
|
type="primary"
|
||||||
loading={loading}
|
loading={loading}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const values = createForm.getFieldsValue();
|
void createForm.validateFields().then((values) => {
|
||||||
void invoke('/admin/redeem/debug/create-token', values, setCreateResult, '核销码已生成');
|
void invoke('/admin/redeem/debug/create-token', values, setCreateResult, '核销码已生成');
|
||||||
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
生成核销码
|
生成核销码
|
||||||
|
|||||||
@@ -102,14 +102,6 @@ export default function MinePage() {
|
|||||||
<span className="mine-member-tag">{hasWechat ? '微信会员' : '好客会员'}</span>
|
<span className="mine-member-tag">{hasWechat ? '微信会员' : '好客会员'}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="mine-settings-btn"
|
|
||||||
aria-label="设置"
|
|
||||||
onClick={() => showToast('preV1:账号设置即将开放')}
|
|
||||||
>
|
|
||||||
<span className="material-symbols-outlined">settings</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="mine-header-glow" aria-hidden />
|
<div className="mine-header-glow" aria-hidden />
|
||||||
</header>
|
</header>
|
||||||
|
|||||||
@@ -13,17 +13,46 @@ export class AdminRedeemDebugService {
|
|||||||
private readonly redeemService: RedeemService,
|
private readonly redeemService: RedeemService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
private parseId(value: string, label: string): bigint {
|
private parseStoreId(value: string): bigint {
|
||||||
const normalized = value?.trim();
|
const normalized = String(value ?? '').trim();
|
||||||
if (!normalized || !/^\d+$/.test(normalized)) {
|
if (!normalized || !/^\d+$/.test(normalized)) {
|
||||||
throw new BadRequestException(`${label}格式无效`);
|
throw new BadRequestException('门店 ID 格式无效');
|
||||||
}
|
}
|
||||||
return BigInt(normalized);
|
return BigInt(normalized);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async resolveUserId(identifier: string): Promise<bigint> {
|
||||||
|
const normalized = String(identifier ?? '').trim();
|
||||||
|
if (!normalized) {
|
||||||
|
throw new BadRequestException('请填写用户 ID、用户编号或手机号');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (/^\d+$/.test(normalized)) {
|
||||||
|
const byId = await this.prisma.user.findUnique({
|
||||||
|
where: { id: BigInt(normalized) },
|
||||||
|
select: { id: true },
|
||||||
|
});
|
||||||
|
if (byId) return byId.id;
|
||||||
|
|
||||||
|
const byPhone = await this.prisma.user.findFirst({
|
||||||
|
where: { phone: normalized },
|
||||||
|
select: { id: true },
|
||||||
|
});
|
||||||
|
if (byPhone) return byPhone.id;
|
||||||
|
} else {
|
||||||
|
const byNo = await this.prisma.user.findFirst({
|
||||||
|
where: { userNo: normalized },
|
||||||
|
select: { id: true },
|
||||||
|
});
|
||||||
|
if (byNo) return byNo.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new NotFoundException('用户不存在,请检查 ID、用户编号或手机号');
|
||||||
|
}
|
||||||
|
|
||||||
private async resolveStoreAccountId(storeId: string): Promise<bigint> {
|
private async resolveStoreAccountId(storeId: string): Promise<bigint> {
|
||||||
const account = await this.prisma.storeAccount.findFirst({
|
const account = await this.prisma.storeAccount.findFirst({
|
||||||
where: { storeId: this.parseId(storeId, '门店 ID'), status: 'ACTIVE' },
|
where: { storeId: this.parseStoreId(storeId), status: 'ACTIVE' },
|
||||||
orderBy: { id: 'asc' },
|
orderBy: { id: 'asc' },
|
||||||
select: { id: true, store: { select: { name: true } } },
|
select: { id: true, store: { select: { name: true } } },
|
||||||
});
|
});
|
||||||
@@ -34,10 +63,11 @@ export class AdminRedeemDebugService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async createToken(dto: AdminRedeemDebugCreateTokenDto) {
|
async createToken(dto: AdminRedeemDebugCreateTokenDto) {
|
||||||
return this.redeemService.createToken(this.parseId(dto.userId, '用户 ID'), {
|
const userId = await this.resolveUserId(dto.userId);
|
||||||
|
return this.redeemService.createToken(userId, {
|
||||||
amount: dto.amount,
|
amount: dto.amount,
|
||||||
couponId: dto.couponId,
|
couponId: dto.couponId?.trim() || undefined,
|
||||||
storeId: dto.storeId,
|
storeId: dto.storeId?.trim() || undefined,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user