feat(ops): add global test whitelist and exclude test accounts from settlement

Unify product/store visibility on HQ whitelist, mark isTest snapshots, and fix SUPER_ADMIN access for the new module.
This commit is contained in:
2026-08-07 15:46:23 +08:00
parent 10fa361983
commit b626db5d84
47 changed files with 1823 additions and 308 deletions
+25 -6
View File
@@ -1,5 +1,5 @@
import { useState } from 'react';
import { Button, Drawer, Form, Input, Select, Table, Tag, Typography } from 'antd';
import { Button, Checkbox, Drawer, Form, Input, Select, Table, Tag, Typography } from 'antd';
import type { ColumnsType } from 'antd/es/table';
import { REDEEM_CHANNEL_LABELS, type RedeemChannel } from '@dukang/shared-types';
import RedeemRecordDetailDescriptions from '../components/RedeemRecordDetailDescriptions';
@@ -14,6 +14,7 @@ type Row = {
settleAmount: number;
channel?: RedeemChannel;
createdAt: string;
isTest?: boolean;
user?: { userNo: string; phone: string | null; nickname?: string | null };
store?: { name: string; cityName: string };
coupon?: { couponNo: string };
@@ -26,14 +27,15 @@ function maskPhone(phone: string | null | undefined) {
export default function RedeemRecordsPage() {
const [form] = Form.useForm();
const [filters, setFilters] = useState<Record<string, string>>({});
const [filters, setFilters] = useState<Record<string, string | boolean>>({});
const { data, loading, page, pageSize, setPage, setPageSize } = useAdminList<Row>(
'/admin/redeem-records',
() => {
const qs = new URLSearchParams();
if (filters.redeemNo) qs.set('redeemNo', filters.redeemNo);
if (filters.storeId) qs.set('storeId', filters.storeId);
if (filters.channel) qs.set('channel', filters.channel);
if (filters.redeemNo) qs.set('redeemNo', String(filters.redeemNo));
if (filters.storeId) qs.set('storeId', String(filters.storeId));
if (filters.channel) qs.set('channel', String(filters.channel));
if (filters.excludeTest) qs.set('excludeTest', 'true');
return qs;
},
[filters],
@@ -53,7 +55,21 @@ export default function RedeemRecordsPage() {
}
const columns: ColumnsType<Row> = [
{ title: '核销号', dataIndex: 'redeemNo', width: 180 },
{
title: '核销号',
dataIndex: 'redeemNo',
width: 200,
render: (v, row) => (
<span>
{v}
{row.isTest ? (
<Tag color="orange" style={{ marginLeft: 6 }}>
</Tag>
) : null}
</span>
),
},
{
title: '方式',
dataIndex: 'channel',
@@ -124,6 +140,9 @@ export default function RedeemRecordsPage() {
]}
/>
</Form.Item>
<Form.Item name="excludeTest" valuePropName="checked">
<Checkbox></Checkbox>
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit">