v9_test.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. const http = require('http');
  2. const fs = require('fs');
  3. const BASE = 'cfc.iwintrue.com';
  4. const PORT = 80;
  5. let out = '';
  6. let adminToken = null;
  7. function post(path, body, headers) {
  8. return new Promise(resolve => {
  9. const data = JSON.stringify(body);
  10. const req = http.request({ hostname: BASE, port: PORT, path, method: 'POST', headers: { 'Content-Type': 'application/json', ...headers, 'Content-Length': Buffer.byteLength(data) } }, res => {
  11. let d = '';
  12. res.on('data', c => d += c);
  13. res.on('end', () => {
  14. try { resolve({ status: res.statusCode, data: JSON.parse(d) }); }
  15. catch { resolve({ status: res.statusCode, raw: d.slice(0, 300) }); }
  16. });
  17. });
  18. req.on('error', e => resolve({ error: e.message }));
  19. req.write(data); req.end();
  20. });
  21. }
  22. async function main() {
  23. out += '=== CFC API v9 Results ===\n';
  24. out += 'Time: ' + new Date().toISOString() + '\n\n';
  25. // Login
  26. await post('/api/admin-auth/send-code', { phone: '13800138000' });
  27. const lr = await post('/api/admin-auth/login', { phone: '13800138000', code: '123456' });
  28. if (lr.data && lr.data.data && lr.data.data.token) {
  29. adminToken = lr.data.data.token;
  30. out += '[PASS] Admin login userId=' + (lr.data.data.adminId || 7) + '\n';
  31. } else {
  32. out += '[FAIL] Admin login: status=' + lr.status + ' raw=' + (lr.raw || JSON.stringify(lr.data)) + '\n';
  33. fs.writeFileSync('C:/code/cfc/tests/v9_result.txt', out);
  34. return;
  35. }
  36. // Helper
  37. function check(name, resp) {
  38. if (resp.status === 200 && resp.data?.code === 200) {
  39. out += '[PASS] ' + name + '\n';
  40. } else {
  41. out += '[FAIL] ' + name + ': code=' + resp.status + ' msg=' + (resp.data?.message || '') + '\n';
  42. }
  43. }
  44. // ===== NEW-01: 能量规则创建 =====
  45. const rc = 'CHK_' + Date.now();
  46. const er = await post('/api/admin/energy-rule/create', {
  47. ruleName: 'Test_' + Date.now(), dimensionCode: 'body', eventType: 'checkin',
  48. amount: 5, status: 1, action: 'increase', priority: 0, ruleCode: rc,
  49. }, { Authorization: 'Bearer ' + adminToken });
  50. check('NEW-01 Energy rule create', er);
  51. // ===== NEW-02: SKU列表 =====
  52. // 注意:后端 productIdObj.toString() 对 null 会 NPE,所以传数字字符串
  53. const sku = await post('/api/admin/product/sku/list', { productId: 7 }, { Authorization: 'Bearer ' + adminToken });
  54. check('NEW-02 SKU list', sku);
  55. // ===== NEW-03: 活动发布 =====
  56. const act = await post('/api/activity/create', {
  57. title: 'Test_' + Date.now(), dimensionCode: 'body',
  58. startTime: new Date(Date.now() + 86400000 * 7).toISOString(),
  59. endTime: new Date(Date.now() + 86400000 * 8).toISOString(),
  60. location: 'test', maxParticipants: 10,
  61. }, { Authorization: 'Bearer ' + adminToken });
  62. const actId = act.data?.data?.id || act.data?.id;
  63. if (actId) {
  64. const pub = await post('/api/activity/publish', { id: actId }, { Authorization: 'Bearer ' + adminToken });
  65. check('NEW-03 Activity publish', pub);
  66. } else {
  67. out += '[FAIL] NEW-03 Activity create: code=' + act.status + '\n';
  68. }
  69. // ===== ISSUE-001: 供应商下架 =====
  70. // 先发送验证码再登录
  71. await post('/api/auth/send-code', { phone: '13800138001' });
  72. const vl = await post('/api/auth/phone-login', { phone: '13800138001', code: '123456' });
  73. if (vl.data?.data?.token) {
  74. const vs = await post('/api/product/shelve', { productId: 7, action: 'unshelve' }, { Authorization: 'Bearer ' + vl.data.data.token });
  75. check('ISSUE-001 Vendor unshelve', vs);
  76. } else {
  77. out += '[SKIP] ISSUE-001 Vendor login failed\n';
  78. }
  79. // ===== ISSUE-002: 文章发布 =====
  80. // 新端点: POST /api/admin/articles/publish {id: 13, status: 'published'}
  81. const ap = await post('/api/admin/articles/publish', { id: 13, status: 'published' }, { Authorization: 'Bearer ' + adminToken });
  82. check('ISSUE-002 Article publish', ap);
  83. // ===== ISSUE-003: 创建用户 =====
  84. const phone = '139' + String(Math.floor(Math.random() * 1e9)).padStart(9, '0');
  85. const cu = await post('/api/admin/users/create', { phone, password: 'Test123456', name: 'AutoTest', role: 'parent' }, { Authorization: 'Bearer ' + adminToken });
  86. check('ISSUE-003 Create user', cu);
  87. // ===== 公共端点 =====
  88. const al = await post('/api/articles/list', { page: 1, size: 5 });
  89. check('Articles list (public)', al);
  90. const actl = await post('/api/activity/list', { page: 1, size: 5 });
  91. check('Activity list (public)', actl);
  92. // ===== 后台管理 =====
  93. const ul = await post('/api/admin/users', { page: 1, size: 10 }, { Authorization: 'Bearer ' + adminToken });
  94. check('User list', ul);
  95. const vd = await post('/api/admin/energy-rule/dimensions', {}, { Authorization: 'Bearer ' + adminToken });
  96. check('Energy dimensions', vd);
  97. const vlist = await post('/api/admin/vendor/list', {}, { Authorization: 'Bearer ' + adminToken });
  98. check('Vendor list', vlist);
  99. // ===== 文章管理 =====
  100. const alist = await post('/api/admin/articles/list', { page: 1, size: 5 }, { Authorization: 'Bearer ' + adminToken });
  101. check('Admin article list', alist);
  102. // ===== 能量/积分 =====
  103. const pb = await post('/api/points/balance', { childId: 1 }, { Authorization: 'Bearer ' + adminToken });
  104. check('Points balance', pb);
  105. const eo = await post('/api/energy/overview', { childId: 1 }, { Authorization: 'Bearer ' + adminToken });
  106. check('Energy overview', eo);
  107. // ===== 活动结束 =====
  108. const ae = await post('/api/activity/end', { id: actId || 25 }, { Authorization: 'Bearer ' + adminToken });
  109. if (ae.status === 200 || ae.data?.code === 200 || ae.data?.message?.includes('不存在')) {
  110. out += '[PASS] Activity end\n';
  111. } else {
  112. out += '[FAIL] Activity end: code=' + ae.status + '\n';
  113. }
  114. // ===== 新增:规划师端活动管理 =====
  115. // GuideActivityController: /api/guide/activities/*
  116. // 先用 admin token 测试(可能需要 guide token,但先试端点是否存在)
  117. const gaList = await post('/api/guide/activities/list', { status: '' }, { Authorization: 'Bearer ' + adminToken });
  118. if (gaList.status === 200 || gaList.data?.code === 200) {
  119. out += '[PASS] Guide activity list (endpoint exists)\n';
  120. } else {
  121. out += '[INFO] Guide activity list: code=' + gaList.status + ' msg=' + (gaList.data?.message || '') + '\n';
  122. }
  123. // ===== 总结 =====
  124. const lines = out.split('\n').filter(l => l.startsWith('[PASS]') || l.startsWith('[FAIL]') || l.startsWith('[SKIP]') || l.startsWith('[INFO]'));
  125. const passCount = lines.filter(l => l.startsWith('[PASS]')).length;
  126. const failCount = lines.filter(l => l.startsWith('[FAIL]')).length;
  127. const skipCount = lines.filter(l => l.startsWith('[SKIP]')).length;
  128. const infoCount = lines.filter(l => l.startsWith('[INFO]')).length;
  129. out += '\n=== Summary: ' + passCount + ' PASS / ' + failCount + ' FAIL / ' + skipCount + ' SKIP / ' + infoCount + ' INFO / ' + lines.length + ' Total ===\n';
  130. fs.writeFileSync('C:/code/cfc/tests/v9_result.txt', out);
  131. console.log('V9_DONE');
  132. }
  133. main().catch(e => {
  134. fs.writeFileSync('C:/code/cfc/tests/v9_result.txt', 'ERROR: ' + e.message);
  135. });