qiwei-subscription-run.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. const { readQiweiAuthToken, readQiweiApiBase } = require('../core/credentials');
  2. const { callFmodeWecomGateway, redactSecret } = require('../providers/fmode-wecom-gateway');
  3. const { okResult, errorResult } = require('../core/result-envelope');
  4. function authRequiredResult() {
  5. return {
  6. status: 'needs_auth',
  7. assistantMessage: '还没有找到 Fmode 鉴权 token,无法调用由 Fmode 网关转发的企业微信接口。请配置 QIWEI_AUTH_TOKEN、FMODE_API_KEY 或平台 sessionToken 后重试。',
  8. summary: { errorKind: 'missing_auth_token', recoverable: true },
  9. data: {},
  10. files: [],
  11. nextActions: ['配置 Fmode 鉴权 token 后重试'],
  12. warnings: [],
  13. errors: []
  14. };
  15. }
  16. function subscriptionError(error, stage) {
  17. const safeMessage = redactSecret(error && (error.bizMessage || error.message));
  18. const kind = String((error && error.kind) || 'upstream');
  19. if (kind === 'auth') return authRequiredResult();
  20. if (kind === 'billing') {
  21. return {
  22. status: 'needs_recharge',
  23. assistantMessage: `飞马余额不足或订阅扣费失败(${safeMessage})。请充值后重新调用 qiwei_subscribe。`,
  24. summary: { stage, errorKind: kind, recoverable: true },
  25. data: {},
  26. files: [],
  27. nextActions: ['充值飞马余额', '重新调用 qiwei_subscribe'],
  28. warnings: [],
  29. errors: []
  30. };
  31. }
  32. return errorResult(`企微订阅操作失败:${safeMessage}`, {
  33. summary: { stage, errorKind: kind },
  34. nextActions: ['稍后重试', '若持续失败,请检查 Fmode 网关的企业微信接口状态']
  35. });
  36. }
  37. async function qiweiSubscriptionStatus(input = {}) {
  38. const token = readQiweiAuthToken(input);
  39. if (!token) return authRequiredResult();
  40. try {
  41. const result = await callFmodeWecomGateway({
  42. gatewayPath: '/subscribe/status',
  43. httpMethod: 'GET',
  44. token,
  45. apiBase: readQiweiApiBase(input),
  46. cacheBust: true
  47. });
  48. const data = result.data || {};
  49. return okResult({
  50. assistantMessage: data.subscribed
  51. ? `企微包月订阅有效:${data.usedSeats ?? 0}/${data.seats ?? 0} 个席位已使用,到期时间 ${data.expireAt || '未知'}。`
  52. : '企微包月订阅尚未开通或已到期,请调用 qiwei_subscribe 开通或续费。',
  53. summary: {
  54. subscribed: Boolean(data.subscribed),
  55. seats: data.seats ?? null,
  56. usedSeats: data.usedSeats ?? null,
  57. expireAt: data.expireAt || null,
  58. autoRenew: data.autoRenew ?? null,
  59. balance: data.balance
  60. },
  61. data,
  62. nextActions: data.subscribed ? [] : ['调用 qiwei_subscribe 开通或续费']
  63. });
  64. } catch (error) {
  65. return subscriptionError(error, 'subscriptionStatus');
  66. }
  67. }
  68. async function qiweiSubscribe(input = {}) {
  69. const token = readQiweiAuthToken(input);
  70. if (!token) return authRequiredResult();
  71. const seats = input.seats === undefined ? undefined : Number(input.seats);
  72. const months = input.months === undefined ? 1 : Number(input.months);
  73. if (seats !== undefined && (!Number.isInteger(seats) || seats < 1)) {
  74. return errorResult('seats 必须是大于等于 1 的整数。');
  75. }
  76. if (!Number.isInteger(months) || months < 1 || months > 12) {
  77. return errorResult('months 必须是 1-12 的整数。');
  78. }
  79. try {
  80. const result = await callFmodeWecomGateway({
  81. gatewayPath: '/subscribe',
  82. body: { ...(seats === undefined ? {} : { seats }), months },
  83. token,
  84. apiBase: readQiweiApiBase(input)
  85. });
  86. const data = result.data || {};
  87. return okResult({
  88. assistantMessage: `企微包月订阅已开通或续费:${data.seats ?? seats ?? 1} 个席位,购买 ${data.months ?? months} 个月,到期时间 ${data.expireAt || '未知'}。`,
  89. summary: {
  90. subscribed: true,
  91. seats: data.seats ?? seats ?? 1,
  92. months: data.months ?? months,
  93. amount: data.amount ?? null,
  94. expireAt: data.expireAt || null,
  95. autoRenew: data.autoRenew ?? null
  96. },
  97. data,
  98. nextActions: ['调用 qiwei_login_start 添加或重新登录企业微信账号']
  99. });
  100. } catch (error) {
  101. return subscriptionError(error, 'subscribe');
  102. }
  103. }
  104. async function qiweiSubscriptionAutoRenew(input = {}) {
  105. const token = readQiweiAuthToken(input);
  106. if (!token) return authRequiredResult();
  107. if (typeof input.autoRenew !== 'boolean') return errorResult('autoRenew 必须是 boolean。');
  108. try {
  109. const result = await callFmodeWecomGateway({
  110. gatewayPath: '/subscribe/autoRenew',
  111. body: { autoRenew: input.autoRenew },
  112. token,
  113. apiBase: readQiweiApiBase(input)
  114. });
  115. const data = result.data || {};
  116. return okResult({
  117. assistantMessage: `企微订阅自动续费已${data.autoRenew ? '开启' : '关闭'}。`,
  118. summary: { autoRenew: Boolean(data.autoRenew) },
  119. data
  120. });
  121. } catch (error) {
  122. return subscriptionError(error, 'subscriptionAutoRenew');
  123. }
  124. }
  125. module.exports = {
  126. qiweiSubscriptionStatus,
  127. qiweiSubscribe,
  128. qiweiSubscriptionAutoRenew
  129. };