verify-newuser-e2e.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. /**
  2. * 新用户 E2E 扣费验证脚本
  3. *
  4. * 前置:用一个**没用过**的手机号在 https://app.fmode.cn/dev/apig-pay/ 登录+支付
  5. * 把登录后的 sessionToken 填到下面 NEW_TOKEN
  6. *
  7. * 运行:
  8. * node scripts/tools/verify-newuser-e2e.js <sessionToken>
  9. *
  10. * 脚本动作:
  11. * 1. GET /parse/users/me?include=company → 验证新 user 有没有 company
  12. * 2. 列出该 user 名下所有 APIGAuth(应当只有 user 指针,无 company)
  13. * 3. 调用 /api/voc-ecom/forward → 预期 200(符合陈经理说的正常逻辑)
  14. * 4. 再次查 APIGAuth → 验证 count-1 / used+1
  15. */
  16. const TOKEN = process.argv[2];
  17. if (!TOKEN) {
  18. console.error('用法: node verify-newuser-e2e.js <sessionToken>');
  19. process.exit(1);
  20. }
  21. const API = 'https://server.fmode.cn';
  22. const H = { 'X-Parse-Application-Id': 'ncloudmaster', 'X-Parse-Session-Token': TOKEN };
  23. function line() { console.log('━'.repeat(60)); }
  24. (async () => {
  25. line();
  26. console.log(' STEP 1. 查看新 user 信息');
  27. line();
  28. const me = await (await fetch(`${API}/parse/users/me?include=company`, { headers: H })).json();
  29. if (!me.objectId) {
  30. console.error('❌ Token 无效:', me);
  31. process.exit(1);
  32. }
  33. console.log(` userId: ${me.objectId}`);
  34. console.log(` username: ${me.username}`);
  35. console.log(` mobile: ${me.mobile || '(无)'}`);
  36. console.log(` company: ${me.company ? me.company.objectId : '(无) ✅ 正是我们要的新用户'}`);
  37. const userId = me.objectId;
  38. const companyId = me.company?.objectId;
  39. line();
  40. console.log(' STEP 2. 查该 user 名下所有 APIGAuth');
  41. line();
  42. const q = encodeURIComponent(JSON.stringify({
  43. user: { __type: 'Pointer', className: '_User', objectId: userId }
  44. }));
  45. const auths = await (await fetch(
  46. `${API}/parse/classes/APIGAuth?where=${q}&include=api&limit=20`,
  47. { headers: H }
  48. )).json();
  49. if (!auths.results || auths.results.length === 0) {
  50. console.log(' (该 user 还没有任何 APIGAuth —— 需要先去 apig-pay 支付一次)');
  51. } else {
  52. auths.results.forEach(a => {
  53. console.log(` ${a.objectId} api=${a.api?.objectId}(${a.api?.title}) count=${a.count} used=${a.used} company=${a.company?.objectId || '(无)'}`);
  54. });
  55. }
  56. const ecomAuth = auths.results?.find(a => a.api?.objectId === '7HwdQZk55B');
  57. if (!ecomAuth) {
  58. console.log('\n⚠️ 未找到电商 APIG(7HwdQZk55B) 的 APIGAuth,请先在 apig-pay 充值电商服务再跑此脚本');
  59. process.exit(0);
  60. }
  61. console.log(`\n → 使用 APIGAuth ${ecomAuth.objectId}, 余额 ${ecomAuth.count}`);
  62. line();
  63. console.log(' STEP 3. 调用 /api/voc-ecom/forward');
  64. line();
  65. const t0 = Date.now();
  66. const callResp = await fetch(`${API}/api/voc-ecom/forward`, {
  67. method: 'POST',
  68. headers: {
  69. 'Content-Type': 'application/json',
  70. 'Authorization': 'Bearer ' + TOKEN
  71. },
  72. body: JSON.stringify({
  73. path: '/api/KeywordQuery',
  74. method: 'POST',
  75. query: { domain: 1 },
  76. body: { Keyword: 'natural reed diffuser' }
  77. })
  78. });
  79. const cost = Date.now() - t0;
  80. const result = await callResp.text();
  81. console.log(` HTTP ${callResp.status} (${cost}ms)`);
  82. console.log(` Body: ${result.substring(0, 400)}`);
  83. if (callResp.status !== 200) {
  84. console.log('\n❌ 接口没打通。分析:');
  85. if (callResp.status === 403) {
  86. if (companyId) {
  87. console.log(' - user 有 company → 后端走 company 分支');
  88. console.log(` - company ${companyId} 下应该查不到 APIGAuth → 403 符合后端正常逻辑`);
  89. console.log(` - 解决:apig-pay 应该按 company 维度写 APIGAuth(带 company 指针)`);
  90. } else {
  91. console.log(' - user 无 company → 后端应走 user 分支');
  92. console.log(' - 但仍 403 表示 user 维度 APIGAuth 也查不到,或者后端还有其他校验');
  93. }
  94. }
  95. process.exit(1);
  96. }
  97. line();
  98. console.log(' STEP 4. 再查余额,验证扣费');
  99. line();
  100. await new Promise(r => setTimeout(r, 1500));
  101. const after = await (await fetch(
  102. `${API}/parse/classes/APIGAuth/${ecomAuth.objectId}?keys=count,used`,
  103. { headers: H }
  104. )).json();
  105. const dCount = after.count - ecomAuth.count;
  106. const dUsed = after.used - ecomAuth.used;
  107. console.log(` count: ${ecomAuth.count} → ${after.count} (Δ=${dCount})`);
  108. console.log(` used: ${ecomAuth.used} → ${after.used} (Δ=${dUsed})`);
  109. if (dCount === -1 && dUsed === 1) {
  110. console.log('\n🎉 扣费链路打通!新用户流程端到端验证通过');
  111. } else {
  112. console.log('\n⚠️ 接口 200 但余额未按预期变动,检查后端扣费逻辑');
  113. }
  114. })().catch(e => console.error('[FATAL]', e));