用户删除

This commit is contained in:
Yeomai committed 2026-10-04 18:10:28 +08:00
1 parent 0483b14e9b
commit b75ce5b490
5 files changed
+155 -2

No files matched your search

@@ -114,6 +114,12 @@ export class AdminController {
return this.admin.updateUser(env, id, parsed.data);
}
/** 删除用户记录。只会删 users 这一行,其他表不动。 */
@Delete('users/:id')
deleteUser(@AdminEnvParam() env: DataSourceEnv, @Param('id', ParseIntPipe) id: number) {
return this.admin.deleteUser(env, id);
}
// ── 通用内容 CRUD ──────────────────────────────────────────────────────
@Get('data/:resource')
@@ -21,6 +21,7 @@ const NOTIFY_EVENT_LABEL: Record<string, string> = {
xpay_complaint_notify: '用户投诉',
xpay_wxpay_callback_notify: '微信支付风控事件',
user_enter_tempsession: '用户进入客服会话',
admin_delete_user: '后台删除用户',
};
/** 客服消息在日志里的 event 形如 cs_message:text,按前缀归成一类展示。 */
@@ -223,7 +224,7 @@ export class AdminService {
orderBy,
skip: (current - 1) * size,
take: size,
include: { _count: { select: { entitlements: true } } },
include: { _count: { select: { entitlements: true, orders: true } } },
}),
db.user.count({ where }),
]);
@@ -240,6 +241,7 @@ export class AdminService {
registered_at: u.registered_at,
last_login_at: u.last_login_at,
entitlement_count: u._count.entitlements,
order_count: u._count.orders,
})),
total,
page: current,
@@ -251,6 +253,79 @@ export class AdminService {
return this.prisma.clientFor(env).user.update({ where: { id }, data: input });
}
/**
* 删除用户,**连带它的订单和权益一起删**。
*
* 为什么要连带:外键是 RESTRICT,不先删子表就删不掉主表。
* 三个写操作放同一个事务,要么全成功要么都不动。
*
* 删掉之后,对方用同一个微信再次打开小程序会重新注册成新用户(openid 唯一,自动新建)。
*
* 注:refund_requests 对 orders 的外键是 SET NULL,所以那些申请会留着、
* 但指向的订单没了(后台显示「未匹配到订单」)。
*/
async deleteUser(
env: DataSourceEnv,
id: number,
): Promise<{ orders: number; entitlements: number }> {
const db = this.prisma.clientFor(env);
// 先把要删的东西连明细一起查出来——删完就查不到了,日志要靠这份数据
const [user, orders, entitlements] = await Promise.all([
db.user.findUnique({ where: { id } }),
db.order.findMany({
where: { user_id: id },
include: { course: { select: { title: true } } },
orderBy: { created_at: 'asc' },
}),
db.entitlement.findMany({
where: { user_id: id },
include: { course: { select: { title: true } } },
orderBy: { granted_at: 'asc' },
}),
]);
// 记进「通知日志」:点开原始报文能看到删了哪些单、哪些权益。
// 和删除放同一个事务——删除不可撤销,日志是唯一凭据,不能出现「删了但没记上」。
const logData = {
event: 'admin_delete_user',
processed: 1,
err_msg: `后台删除用户 #${id}(连带 ${orders.length} 笔订单、${entitlements.length} 条权益)`,
raw_body: JSON.stringify(
{
用户: { id, openid: user?.openid ?? null, nickname: user?.nickname ?? null },
订单: orders.map((o) => ({
订单号: o.out_trade_no,
课程: o.course?.title ?? null,
金额分: o.amount_fen,
状态: o.status,
终端: o.terminal,
下单时间: o.created_at,
已退款时间: o.refunded_at,
})),
权益: entitlements.map((e) => ({
课程: e.course?.title ?? null,
来源: e.source,
授予时间: e.granted_at,
到期时间: e.expire_at,
备注: e.note,
})),
},
null,
2,
),
};
await db.$transaction([
db.entitlement.deleteMany({ where: { user_id: id } }),
db.order.deleteMany({ where: { user_id: id } }),
db.user.delete({ where: { id } }),
db.paymentNotifyLog.create({ data: logData }),
]);
return { orders: orders.length, entitlements: entitlements.length };
}
// ── 通用内容 CRUD ──────────────────────────────────────────────────────
async listResource(env: DataSourceEnv, name: string, page = 1, pageSize = 20, keyword = '', filter: Record<string, unknown> = {}) {