#!/usr/bin/env node import { randomBytes } from 'node:crypto'; const APP_ID = process.env.XIAOSHU_PARSE_APP_ID || '7pIbDBJmKx_main'; const MASTER_KEY = process.env.XIAOSHU_MASTER_KEY || ''; const PARSE_URL = (process.env.XIAOSHU_PARSE_URL || 'https://server.xiaoshu.pro/parse').replace(/\/$/, ''); const FUNCTION_URL = (process.env.XIAOSHU_FUNCTION_URL || 'https://server.xiaoshu.pro/api/functions').replace(/\/$/, ''); const COMPANY_ID = '7pIbDBJmKx'; if (!MASTER_KEY) throw new Error('缺少 XIAOSHU_MASTER_KEY'); const appHeaders = { 'X-Parse-Application-Id': APP_ID, 'Content-Type': 'application/json' }; const masterHeaders = { ...appHeaders, 'X-Parse-Master-Key': MASTER_KEY }; const username = `codex_member_resource_${Date.now()}`; const password = randomBytes(24).toString('base64url'); const legacyUserId = 970000000 + Math.floor(Date.now() / 1000) % 20000000; let userObjectId = ''; let membershipTargetUserId = ''; let commerceProductId = 0; const created = []; async function request(url, init = {}, expected = 200) { const response = await fetch(url, init); const payload = await response.json().catch(() => ({})); if (response.status !== expected) throw new Error(`${url} 期望 ${expected},实际 ${response.status}: ${payload.retmsg || payload.error || ''}`); return payload; } async function master(path, init = {}, expected = 200) { return request(`${PARSE_URL}${path}`, { ...init, headers: { ...masterHeaders, ...(init.headers || {}) } }, expected); } async function cloud(token, action, params = {}, expected = 200) { return request(`${FUNCTION_URL}/xiaoshu/app/gateway`, { method: 'POST', headers: appHeaders, body: JSON.stringify({ token, params: { action, uid: legacyUserId, ...params } }), }, expected); } async function hardDelete(className, objectId) { if (!objectId) return; await master(`/classes/${className}/${objectId}`, { method: 'DELETE' }).catch(() => undefined); } try { const company = { __type: 'Pointer', className: 'Company', objectId: COMPANY_ID }; const createdUser = await master('/users', { method: 'POST', body: JSON.stringify({ username, password, company, legacyUserId, legacyGroupId: 1, nickname: '会员资料云函数冒烟', legacyUserData: { UserID: legacyUserId, GroupID: 1, HoneyName: '会员资料云函数冒烟' } }), }, 201); userObjectId = createdUser.objectId; const login = await request(`${PARSE_URL}/login`, { method: 'POST', headers: appHeaders, body: JSON.stringify({ username, password }) }); const token = login.sessionToken; await master(`/users/${userObjectId}`, { method: 'PUT', body: JSON.stringify({ legacyUserData: { UserID: legacyUserId, GroupID: 1, HoneyName: '会员资料云函数冒烟', SilverCoin: 200 } }), }); const completedReviewStats = await cloud(token, 'e_get_21list_tj'); if (!Array.isArray(completedReviewStats.result) || Number(completedReviewStats.result[0]?.num) !== 0) throw new Error('本人已完成复习任务统计异常'); const address = await cloud(token, 'receaddr_add', { provinceName: '江西省', cityName: '南昌市', countyName: '青山湖区', userName: '测试收货人', telNumber: '13800138000', detailInfo: '测试路 1 号', postalCode: '330000' }); const addressId = String(address.result?.ID || ''); created.push(['ReceivingAddress', addressId]); if (!addressId || address.result?.ReceivName !== '测试收货人') throw new Error('收货地址新增响应异常'); const addressList = await cloud(token, 'receaddr_list', { cpage: 1, psize: 20 }); if (addressList.page?.itemCount !== 1 || addressList.result?.[0]?.ID !== addressId) throw new Error('收货地址列表异常'); const addressUpdated = await cloud(token, 'receaddr_upd', { id: addressId, provinceName: '江西省', cityName: '南昌市', countyName: '青山湖区', userName: '更新收货人', telNumber: '13800138000', detailInfo: '测试路 2 号', postalCode: '330000' }); if (addressUpdated.result?.ReceivName !== '更新收货人') throw new Error('收货地址更新异常'); const addressGet = await cloud(token, 'receaddr_get', { id: addressId }); if (addressGet.result?.Street !== '测试路 2 号') throw new Error('收货地址详情异常'); const invoice = await cloud(token, 'invoice_add', { aud: 1, name: '测试企业', idnum: '91360100TEST000001', mobile: '13800138000', email: 'billing@example.test', address: '测试注册地址', bankname: '测试银行', bankusr: '6222020000000000000' }); const invoiceId = String(invoice.result?.ID || ''); created.push(['InvoiceProfile', invoiceId]); if (!invoiceId || invoice.result?.InvHead !== '测试企业') throw new Error('发票资料新增响应异常'); const invoiceUpdated = await cloud(token, 'invoice_upd', { id: invoiceId, aud: 1, name: '更新测试企业', idnum: '91360100TEST000001', mobile: '13800138000', email: 'billing@example.test', address: '测试注册地址', bankname: '测试银行', bankusr: '6222020000000000000' }); if (invoiceUpdated.result?.InvHead !== '更新测试企业') throw new Error('发票资料更新异常'); const invoiceList = await cloud(token, 'invoice_list', { cpage: 1, psize: 20 }); if (invoiceList.page?.itemCount !== 1 || invoiceList.result?.[0]?.ID !== invoiceId) throw new Error('发票资料列表异常'); const bank = await cloud(token, 'user_bank_add', { CardType: '银行卡', BankName: '测试银行', PeopleName: '测试开户人', CardNum: '6222020000000000000', Remark: '首次创建' }); const bankId = String(bank.result?.ID || ''); created.push(['UserBankAccount', bankId]); if (!bankId || bank.result?.PeopleName !== '测试开户人') throw new Error('提现账户新增响应异常'); const bankUpdated = await cloud(token, 'user_bank_add', { id: bankId, CardType: '银行卡', BankName: '测试银行', PeopleName: '更新开户人', CardNum: '6222020000000000000', Remark: '已更新' }); if (bankUpdated.result?.PeopleName !== '更新开户人') throw new Error('提现账户更新异常'); const bankGet = await cloud(token, 'user_bank_get', { id: bankId }); if (bankGet.result?.Remark !== '已更新') throw new Error('提现账户详情异常'); const bankList = await cloud(token, 'user_bank_list', { cpage: 1, psize: 20 }); if (bankList.page?.itemCount !== 1 || bankList.result?.[0]?.ID !== bankId) throw new Error('提现账户列表异常'); const ask = await cloud('', 'vote_ask', { id: 1 }); const questions = await cloud('', 'vote_question', { askid: 1 }); if (ask.result?.[0]?.Title !== '第一组问题' || Number(questions.result?.length) !== 48 || !questions.result?.[0]?.QOption) throw new Error('测评问卷或题目读取异常'); const examStamp = `2099-01-01 00:${String(Date.now() % 60).padStart(2, '0')}:${String(Date.now() % 60).padStart(2, '0')}`; const examAnswers = questions.result.slice(0, 2).map((item) => ({ qid: item.ID, qtype: item.QType, answer: '', result: 2 })); const answer = await cloud(token, 'vote_add', { mu: JSON.stringify({ AskID: 1, Answer: JSON.stringify(examAnswers), UserID: legacyUserId, CDate: examStamp }) }); if (!Number(answer.result)) throw new Error('测评答题记录新增异常'); const answerSourceKey = `app:vote:${legacyUserId}:1:${examStamp}`; const answerRows = await master(`/classes/DesignAnswer?where=${encodeURIComponent(JSON.stringify({ sourceKey: answerSourceKey }))}&limit=2`); if (answerRows.results?.length !== 1 || Number(answerRows.results[0].id) !== Number(answer.result)) throw new Error('测评答题记录写后读或幂等键异常'); created.push(['DesignAnswer', answerRows.results[0].objectId]); const repeatedAnswer = await cloud(token, 'vote_add', { mu: JSON.stringify({ AskID: 1, Answer: JSON.stringify(examAnswers), UserID: legacyUserId, CDate: examStamp }) }); if (Number(repeatedAnswer.result) !== Number(answer.result)) throw new Error('测评答题记录重复提交未保持幂等'); const unit = await cloud(token, 'unit_record_update', { userId: legacyUserId, bookId: 16, nids: '40', wordIds: JSON.stringify({ 40: '2505,2506' }) }); if (unit.result?.updated !== 1 || unit.result?.items?.[0]?.learnedCount !== 2) throw new Error('单元学习进度保存异常'); created.push(['UnitLearningRecord', unit.result.items[0].objectId]); const unitRepeat = await cloud(token, 'unit_record_update', { userId: legacyUserId, bookId: 16, nids: '40', wordIds: JSON.stringify({ 40: '2505' }) }); if (unitRepeat.result?.items?.[0]?.objectId !== unit.result.items[0].objectId || unitRepeat.result?.items?.[0]?.learnedCount !== 1) throw new Error('单元学习进度幂等更新异常'); const favoriteMissing = await cloud(token, 'user_star_is', { id: 'future-product-1', type: 2 }); if (String(favoriteMissing.result) !== '0') throw new Error('收藏初始状态异常'); await cloud(token, 'user_star_add', { id: 'future-product-1', type: 2, title: '未来商品', url: '/Shop/future-product-1' }); const favoriteAdded = await cloud(token, 'user_star_is', { id: 'future-product-1', type: 2 }); if (String(favoriteAdded.result) !== '1') throw new Error('收藏新增后状态异常'); const favoriteRows = await master(`/classes/UserFavorite?where=${encodeURIComponent(JSON.stringify({ sourceKey: `app:favorite:${legacyUserId}:2:future-product-1` }))}&limit=2`); if (favoriteRows.results?.length !== 1) throw new Error('收藏关系未写入规范类'); created.push(['UserFavorite', favoriteRows.results[0].objectId]); await cloud(token, 'user_star_del', { id: 'future-product-1', type: 2 }); const favoriteDeleted = await cloud(token, 'user_star_is', { id: 'future-product-1', type: 2 }); if (String(favoriteDeleted.result) !== '0') throw new Error('取消收藏后状态异常'); const supplierRequest = await cloud(token, 'pub_add', { pubid: 8, model: JSON.stringify({ PubTitle: '冒烟供应商', PubContent: '申请入驻', BankAccount: 'TEST-ONLY', PubIsTrue: 1 }) }); const supplierRequestId = String(supplierRequest.result?.ID || ''); if (!supplierRequestId || supplierRequest.result?.Category !== 'supplier-application') throw new Error('供应商服务申请新增异常'); created.push(['ServiceRequest', supplierRequestId]); const supplierRequests = await cloud(token, 'pub_list', { pubid: 8, cpage: 1, psize: 20 }); if (supplierRequests.page?.itemCount !== 1 || supplierRequests.result?.[0]?.ID !== supplierRequestId || supplierRequests.result?.[0]?.BankAccount !== 'TEST-ONLY') throw new Error('供应商服务申请列表异常'); const returnRequest = await cloud(token, 'pub_add', { pubid: 10, model: JSON.stringify({ PubTitle: '冒烟退货', PubContent: '商品异常', PubContentid: 'order-smoke', PubIsTrue: 1 }) }); const returnRequestId = String(returnRequest.result?.ID || ''); if (!returnRequestId) throw new Error('售后服务申请新增异常'); created.push(['ServiceRequest', returnRequestId]); const returnRequests = await cloud(token, 'pub_list', { pubid: 10, cid: 'order-smoke', cpage: 1, psize: 20 }); if (returnRequests.page?.itemCount !== 1 || returnRequests.result?.[0]?.ID !== returnRequestId) throw new Error('售后服务申请按订单筛选异常'); await cloud(token, 'pub_add', { pubid: 6, model: JSON.stringify({ PubTitle: '无效申请' }) }, 400); const productName = `冒烟商品-${Date.now()}`; const product = await cloud(token, 'product_add', { model: JSON.stringify({ Proname: productName, Nodeid: 7, LinPrice: 12.5, ShiPrice: 20, ProUnit: '件', Stock: 10, Thumbnails: 'https://example.test/product.png', Proinfo: '初始简介', Procontent: '

商品正文

' }), addon: JSON.stringify({ pics: JSON.stringify([{ url: 'https://example.test/product-1.png' }]) }), }); const productId = Number(product.result); commerceProductId = productId; if (!productId) throw new Error('商品新增未返回规范编号'); const productRows = await master(`/classes/CommerceProduct?where=${encodeURIComponent(JSON.stringify({ productId }))}&limit=2`); if (productRows.results?.length !== 1) throw new Error('商品主记录未写入规范类'); const productObjectId = productRows.results[0].objectId; created.push(['CommerceProduct', productObjectId]); const draftProducts = await cloud(token, 'product_list', { uids: legacyUserId, mode: 'unaudit', skey: productName, cpage: 1, psize: 20 }); if (draftProducts.page?.itemCount !== 1 || Number(draftProducts.result?.[0]?.ID) !== productId) throw new Error('待审核商品列表异常'); const ownProduct = await cloud(token, 'product_get', { id: productId }); if (ownProduct.result?.[0]?.Proname !== productName || Number(ownProduct.result?.[0]?.Stock) !== 10) throw new Error('商品详情投影异常'); await cloud(token, 'product_upd', { id: productId, model: JSON.stringify({ ID: productId, Proinfo: '更新简介' }), addon: JSON.stringify({}) }); const updatedProduct = await cloud(token, 'product_get', { id: productId }); if (updatedProduct.result?.[0]?.Proinfo !== '更新简介') throw new Error('商品更新异常'); await master(`/classes/CommerceProduct/${productObjectId}`, { method: 'PUT', body: JSON.stringify({ status: 'published' }) }); const publicProducts = await cloud('', 'product_list', { uid: '', skey: productName, cpage: 1, psize: 20 }); if (publicProducts.page?.itemCount !== 1 || Number(publicProducts.result?.[0]?.ID) !== productId) throw new Error('已发布商品公共列表异常'); const publicProduct = await cloud('', 'product_get', { uid: '', id: productId }); if (publicProduct.result?.[0]?.Proname !== productName) throw new Error('已发布商品公共详情异常'); const stockIn = await cloud(token, 'product_stock_change', { proid: productId, num: 5, remark: '冒烟入库' }); if (Number(stockIn.result?.Stock) !== 15) throw new Error('商品入库原子更新异常'); const stockOut = await cloud(token, 'product_stock_change', { proid: productId, num: -2, remark: '冒烟出库' }); if (Number(stockOut.result?.Stock) !== 13) throw new Error('商品出库原子更新异常'); await cloud(token, 'product_stock_change', { proid: productId, num: -999, remark: '禁止负库存' }, 409); const stockLogs = await cloud(token, 'product_stock_list', { proid: productId, cpage: 1, psize: 20 }); if (stockLogs.page?.itemCount !== 2 || !stockLogs.result?.some((item) => Number(item.StockNow) === 13)) throw new Error(`库存流水异常:${JSON.stringify(stockLogs)}`); for (const row of (await master(`/classes/ProductStockLedger?where=${encodeURIComponent(JSON.stringify({ productId }))}&limit=100`)).results || []) created.push(['ProductStockLedger', row.objectId]); await cloud(token, 'cart_add', { proID: productId, proNum: 1 }); await cloud(token, 'cart_add', { proID: productId, proNum: 2 }); const cart = await cloud(token, 'cart_list', { ids: '0' }); if (cart.result?.CartDT?.length !== 1 || Number(cart.result.CartDT[0]?.Pronum) !== 3 || Number(cart.result.TotalMoney) !== 37.5) throw new Error('购物车合并或金额投影异常'); const cartId = String(cart.result.CartDT[0]?.objectId || ''); if (!cartId) throw new Error('购物车记录 ID 缺失'); created.push(['ShoppingCartItem', cartId]); const selectedCart = await cloud(token, 'cart_list', { ids: cartId }); if (selectedCart.result?.CartDT?.length !== 1) throw new Error('购物车按 ID 筛选异常'); const couponId = 910000000 + Math.floor(Date.now() / 1000) % 80000000; const couponDefinition = await master('/classes/CouponDefinition', { method: 'POST', body: JSON.stringify({ company, couponId, name: '冒烟满减券', amount: 5, minAmount: 20, maxAmount: 100, status: 'active', totalQuantity: 10, claimedQuantity: 0, sourceKey: `app:coupon-smoke:${couponId}`, }), }, 201); created.push(['CouponDefinition', couponDefinition.objectId]); const coupons = await cloud(token, 'coupon_list', { cpage: 1, psize: 100 }); if (!coupons.result?.some((item) => Number(item.Flow) === couponId)) throw new Error('可领取优惠券列表异常'); const claimedCoupon = await cloud(token, 'coupon_usrgot_add', { flowid: couponId }); const userCouponId = String(claimedCoupon.result?.ID || ''); if (!userCouponId || Number(claimedCoupon.result?.Flow) !== couponId) throw new Error('优惠券领取异常'); created.push(['UserCoupon', userCouponId]); const claimedAgain = await cloud(token, 'coupon_usrgot_add', { flowid: couponId }); if (String(claimedAgain.result?.ID) !== userCouponId) throw new Error('重复领取优惠券未保持幂等'); const idleCoupons = await cloud(token, 'coupon_usrgot_list', { fast: 'idle', cpage: 1, psize: 20 }); if (!idleCoupons.result?.some((item) => String(item.ID) === userCouponId)) throw new Error('用户未使用优惠券列表异常'); const checkout = await cloud(token, 'payment_cart', { cartIds: cartId, use_sicon: 1, rece: JSON.stringify({ userName: '测试收货人', telNumber: '13800138000', provinceName: '江西省', cityName: '南昌市', countyName: '青山湖区', detailInfo: '测试路 1 号', postalCode: '330000' }), fareDT: '[]', invoice: '', couponid: userCouponId, }); const orderId = Number(checkout.addon?.orderIds); if (!orderId || String(checkout.addon?.cartIds) !== cartId) throw new Error('学习豆订单结算响应异常'); const repeatedCheckout = await cloud(token, 'payment_cart', { cartIds: cartId, use_sicon: 1, rece: JSON.stringify({ detailInfo: '幂等重试' }), fareDT: '[]', couponid: userCouponId, }); if (Number(repeatedCheckout.addon?.orderIds) !== orderId) throw new Error('订单重复提交未保持幂等'); await cloud(token, 'payment_success', { orderIds: orderId }); await cloud(token, 'payment_success', { orderIds: orderId }); await cloud(token, 'payment_cart_again', { orderId, mode: 1 }); await cloud(token, 'payment_cart_again', { orderId, mode: 2 }, 501); const paidMember = await cloud(token, 'user_get'); if (Number(paidMember.addon?.SilverCoin) !== 167.5) throw new Error(`学习豆扣减异常:${paidMember.addon?.SilverCoin}`); const emptyCartAfterCheckout = await cloud(token, 'cart_list', { ids: '0' }); if (emptyCartAfterCheckout.result?.CartDT?.length) throw new Error('已结算购物车仍可见'); const usedCoupons = await cloud(token, 'coupon_usrgot_list', { fast: 'used', cpage: 1, psize: 20 }); if (!usedCoupons.result?.some((item) => String(item.ID) === userCouponId)) throw new Error('已使用优惠券状态异常'); const order = await cloud(token, 'order_get', { id: orderId }); if (Number(order.result?.ID) !== orderId || Number(order.result?.Ordersamount) !== 32.5 || order.addon?.cart?.length !== 1) throw new Error(`订单详情投影异常:${JSON.stringify(order)}`); const buyerOrders = await cloud(token, 'order_list', { fast: 'all', cpage: 1, psize: 20 }); if (!buyerOrders.result?.some((item) => Number(item.ID) === orderId)) throw new Error('买家订单列表异常'); const sellerOrders = await cloud(token, 'user_shop_order', { fast: 'paid', cpage: 1, psize: 20 }); if (!sellerOrders.result?.some((item) => Number(item.ID) === orderId)) throw new Error('商户待发货订单列表异常'); const sales = await cloud(token, 'user_shop_sales', { fast: 'month', cpage: 1, psize: 5 }); if (!sales.result?.some((item) => Number(item.ID) === productId) || Number(sales.addon) !== 37.5) throw new Error('商户销售汇总异常'); await cloud(token, 'order_delivery', { id: orderId, expComp: '顺丰快递', expNo: 'SF-TEST-0001', expRemind: '冒烟测试' }); const shipped = await cloud(token, 'order_get', { id: orderId }); if (Number(shipped.result?.StateLogistics) !== 1 || shipped.addon?.express?.ExpNo !== 'SF-TEST-0001') throw new Error('订单发货状态异常'); await cloud(token, 'order_signfor', { id: orderId }); const signed = await cloud(token, 'order_get', { id: orderId }); if (Number(signed.result?.Signed) !== 1 || Number(signed.result?.StateLogistics) !== 2) throw new Error('订单签收状态异常'); await cloud(token, 'order_comment_add', { orderId, proId: productId, score: 5, title: '冒烟好评', content: '订单链路验证通过', images: JSON.stringify([{ url: 'https://example.test/comment.png' }]) }); await cloud(token, 'order_comment_add', { orderId, proId: productId, score: 5, title: '重复评价' }, 409); const publicComments = await cloud('', 'order_comment_list', { uid: '', proId: productId, rate: 'good', hasImgOnly: 1, cpage: 1, psize: 20 }); if (publicComments.page?.itemCount !== 1 || publicComments.result?.[0]?.Title !== '冒烟好评') throw new Error('商品评价公共列表异常'); const afterOrderProduct = await cloud(token, 'product_get', { id: productId }); if (Number(afterOrderProduct.result?.[0]?.Stock) !== 10) throw new Error('订单库存扣减异常'); for (const className of ['CommerceOrder', 'CommerceOrderItem', 'CommercePayment', 'ProductComment']) { const orderRows = await master(`/classes/${className}?where=${encodeURIComponent(JSON.stringify(className === 'ProductComment' ? { productId } : { orderId }))}&limit=100`); for (const row of orderRows.results || []) created.push([className, row.objectId]); } const membershipTargetLegacyId = legacyUserId + 1; const membershipTarget = await master('/users', { method: 'POST', body: JSON.stringify({ username: `${username}_agent_target`, password: randomBytes(24).toString('base64url'), company, legacyUserId: membershipTargetLegacyId, legacyGroupId: 1, nickname: '代理库存目标会员', legacyUserData: { UserID: membershipTargetLegacyId, GroupID: 1, ParentUserID: legacyUserId, HoneyName: '代理库存目标会员', boffExp: 0 }, }), }, 201); membershipTargetUserId = membershipTarget.objectId; await master(`/users/${userObjectId}`, { method: 'PUT', body: JSON.stringify({ legacyGroupId: 4, legacyUserData: { UserID: legacyUserId, GroupID: 4, HoneyName: '会员资料云函数冒烟', SilverCoin: 167.5, boffExp: 5000 } }), }); const upgradeKey = `smoke-upgrade-${Date.now()}`; await cloud(token, 'user_group_usr_upgrade', { upusr: membershipTargetLegacyId, gid: 2, idempotencyKey: upgradeKey }); await cloud(token, 'user_group_usr_upgrade', { upusr: membershipTargetLegacyId, gid: 2, idempotencyKey: upgradeKey }); const supplyKey = `smoke-supply-${Date.now()}`; await cloud(token, 'user_group_usr_supply', { upusr: membershipTargetLegacyId, munber: 250, idempotencyKey: supplyKey }); const operatorAfterStock = await master(`/users/${userObjectId}`); const targetAfterStock = await master(`/users/${membershipTargetUserId}`); if (Number(operatorAfterStock.legacyUserData?.boffExp) !== 3750 || Number(targetAfterStock.legacyGroupId) !== 2 || Number(targetAfterStock.legacyUserData?.boffExp) !== 1250) throw new Error('代理招募或库存批发原子余额异常'); const membershipLedgers = await master(`/classes/MembershipStockLedger?where=${encodeURIComponent(JSON.stringify({ operatorLegacyId: legacyUserId }))}&limit=20`); if (membershipLedgers.results?.length !== 2) throw new Error('代理库存流水数量或幂等异常'); for (const row of membershipLedgers.results || []) created.push(['MembershipStockLedger', row.objectId]); await cloud(token, 'product_sale_change', { id: productId, status: 0 }); await cloud('', 'product_get', { uid: '', id: productId }, 404); await cloud(token, 'product_sale_change', { id: productId, status: 1 }); await cloud(token, 'product_del', { id: productId }); const emptyCartAfterRecycle = await cloud(token, 'cart_list', { ids: '0' }); if (emptyCartAfterRecycle.result?.CartDT?.length) throw new Error('已回收商品仍出现在购物车'); await cloud(token, 'receaddr_del', { id: addressId }); await cloud(token, 'invoice_del', { id: invoiceId }); await cloud(token, 'user_bank_del', { id: bankId }); const emptyAddress = await cloud(token, 'receaddr_list', { cpage: 1, psize: 20 }); const emptyInvoice = await cloud(token, 'invoice_list', { cpage: 1, psize: 20 }); const emptyBank = await cloud(token, 'user_bank_list', { cpage: 1, psize: 20 }); if (emptyAddress.page?.itemCount || emptyInvoice.page?.itemCount || emptyBank.page?.itemCount) throw new Error('软删除后的会员资料仍出现在列表'); const auditWhere = encodeURIComponent(JSON.stringify({ legacyUserId })); const audits = await master(`/classes/AppAuditLog?where=${auditWhere}&count=1&limit=0`); if (Number(audits.count) < 30) throw new Error(`会员资料、商城与学习操作审计数量不足:${audits.count || 0}`); const direct = await fetch(`${PARSE_URL}/classes/ReceivingAddress?limit=1`, { headers: { 'X-Parse-Application-Id': APP_ID } }); if (direct.ok) throw new Error('会员敏感资料类未关闭浏览器直接读取权限'); const directServiceRequest = await fetch(`${PARSE_URL}/classes/ServiceRequest?limit=1`, { headers: { 'X-Parse-Application-Id': APP_ID } }); if (directServiceRequest.ok) throw new Error('服务申请类未关闭浏览器直接读取权限'); for (const className of ['CommerceProduct', 'ShoppingCartItem', 'ProductStockLedger', 'ProductComment', 'CommerceOrder', 'CommerceOrderItem', 'CommercePayment', 'CouponDefinition', 'UserCoupon', 'MembershipStockLedger']) { const directCommerce = await fetch(`${PARSE_URL}/classes/${className}?limit=1`, { headers: { 'X-Parse-Application-Id': APP_ID } }); if (directCommerce.ok) throw new Error(`${className} 未关闭浏览器直接读取权限`); } console.log(JSON.stringify({ passed: true, reviewStats: true, address: true, invoice: true, bank: true, vote: true, unitProgress: true, favorite: true, serviceRequests: true, commerceProducts: true, stockLedger: true, cart: true, coupons: true, orders: true, internalPayment: true, comments: true, sellerSales: true, membershipStock: true, audit: Number(audits.count), directClassAccessDenied: true }, null, 2)); } finally { for (const [className, objectId] of created.reverse()) await hardDelete(className, objectId); if (commerceProductId) { const productWhere = encodeURIComponent(JSON.stringify({ productId: commerceProductId })); for (const className of ['ShoppingCartItem', 'ProductStockLedger', 'ProductComment', 'CommerceOrderItem', 'CommerceProduct']) { const rows = await master(`/classes/${className}?where=${productWhere}&limit=1000`).catch(() => ({ results: [] })); for (const row of rows.results || []) await hardDelete(className, row.objectId); } } if (legacyUserId) { const where = encodeURIComponent(JSON.stringify({ legacyUserId })); for (const className of ['CommerceOrder', 'CommercePayment', 'UserCoupon']) { const rows = await master(`/classes/${className}?where=${where}&limit=1000`).catch(() => ({ results: [] })); for (const row of rows.results || []) await hardDelete(className, row.objectId); } const logs = await master(`/classes/AppAuditLog?where=${where}&limit=1000`).catch(() => ({ results: [] })); for (const row of logs.results || []) await hardDelete('AppAuditLog', row.objectId); const membershipWhere = encodeURIComponent(JSON.stringify({ operatorLegacyId: legacyUserId })); const membershipRows = await master(`/classes/MembershipStockLedger?where=${membershipWhere}&limit=1000`).catch(() => ({ results: [] })); for (const row of membershipRows.results || []) await hardDelete('MembershipStockLedger', row.objectId); } if (userObjectId) { const pointer = { __type: 'Pointer', className: '_User', objectId: userObjectId }; const where = encodeURIComponent(JSON.stringify({ user: pointer })); const sessions = await master(`/sessions?where=${where}&limit=1000`).catch(() => ({ results: [] })); for (const row of sessions.results || []) await master(`/sessions/${row.objectId}`, { method: 'DELETE' }).catch(() => undefined); await master(`/users/${userObjectId}`, { method: 'DELETE' }).catch(() => undefined); } if (membershipTargetUserId) await master(`/users/${membershipTargetUserId}`, { method: 'DELETE' }).catch(() => undefined); }