feat(account): self-service deletion with a 7-day cooling-off period
Request signs out everywhere and freezes paid routes (423 + DB triggers); signing in within 7 days shows the pending gate with 撤销注销. A periodic idempotent worker purges personal content afterwards and keeps finance rows against a tombstoned identity. Read-only admin list. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N4f2nya58RoRu4yEmJgRGE
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
3c8123d912
commit
96adbce3a9
@@ -0,0 +1,44 @@
|
||||
"use client";
|
||||
|
||||
import { Descriptions, type TableColumnsType } from "antd";
|
||||
|
||||
import { formatAdminDate, ResourceTable } from "@/components/admin/resource-table";
|
||||
|
||||
type DeletionRecord = {
|
||||
id: string;
|
||||
userId: string;
|
||||
status: string;
|
||||
requestedAt: string;
|
||||
scheduledFor: string;
|
||||
cancelledAt: string | null;
|
||||
completedAt: string | null;
|
||||
attemptCount: number;
|
||||
errorCode: string | null;
|
||||
};
|
||||
|
||||
const STATUS_LABELS: Record<string, string> = { pending: "冷静期中", cancelled: "已撤销", completed: "已删除" };
|
||||
|
||||
const columns: TableColumnsType<DeletionRecord> = [
|
||||
{ title: "状态", dataIndex: "status", sorter: true, render: (value: string) => STATUS_LABELS[value] ?? value },
|
||||
{ title: "用户 ID", dataIndex: "userId" },
|
||||
{ title: "申请时间", dataIndex: "requestedAt", sorter: true, render: formatAdminDate },
|
||||
{ title: "计划删除", dataIndex: "scheduledFor", sorter: true, render: formatAdminDate },
|
||||
{ title: "撤销时间", dataIndex: "cancelledAt", render: (value: string | null) => (value ? formatAdminDate(value) : "—") },
|
||||
{ title: "完成时间", dataIndex: "completedAt", render: (value: string | null) => (value ? formatAdminDate(value) : "—") },
|
||||
{ title: "失败次数", dataIndex: "attemptCount" },
|
||||
{ title: "最近错误", dataIndex: "errorCode", render: (value: string | null) => value ?? "—" },
|
||||
];
|
||||
|
||||
export default function AccountDeletionsPage() {
|
||||
return <ResourceTable<DeletionRecord>
|
||||
resource="account-deletions"
|
||||
title="注销申请(只读)"
|
||||
columns={columns}
|
||||
statusOptions={[
|
||||
{ label: "冷静期中", value: "pending" },
|
||||
{ label: "已撤销", value: "cancelled" },
|
||||
{ label: "已删除", value: "completed" },
|
||||
]}
|
||||
extra={<Descriptions size="small" items={[{ key: "policy", label: "说明", children: "7 天冷静期后自动删除个人内容;订单与点数流水保留但不再关联身份。本表不存邮箱与昵称。" }]} />}
|
||||
/>;
|
||||
}
|
||||
@@ -104,6 +104,7 @@ export function AdminApp({ children }: { children: ReactNode }) {
|
||||
{ name: "administrators", list: "/admin/administrators", meta: { label: "管理员", icon: <SafetyCertificateOutlined /> } },
|
||||
{ name: "roles", list: "/admin/roles", meta: { label: "角色权限", icon: <TeamOutlined /> } },
|
||||
{ name: "customers", list: "/admin/customers", meta: { label: "用户资料", icon: <UserOutlined /> } },
|
||||
{ name: "account-deletions", list: "/admin/account-deletions", meta: { label: "注销申请", icon: <UserOutlined /> } },
|
||||
{ name: "products", list: "/admin/products", meta: { label: "商品权益", icon: <ShoppingOutlined /> } },
|
||||
{ name: "subscriptions", list: "/admin/subscriptions", meta: { label: "订阅", icon: <CreditCardOutlined /> } },
|
||||
{ name: "orders", list: "/admin/orders", meta: { label: "订单", icon: <DatabaseOutlined /> } },
|
||||
|
||||
Reference in New Issue
Block a user