set-parser.js 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. /**
  2. * 语句:set 设置变量 + 变量与值解析(原 value-resolver:extractVarName、replaceVariablesInString、resolveValue、parseValue、时间/延迟)
  3. */
  4. const { assertStrictKeys } = require('../action-schema.js')
  5. const types = ['set']
  6. function extractVarName(varName) {
  7. if (typeof varName === 'string' && varName.startsWith('{') && varName.endsWith('}')) {
  8. return varName.slice(1, -1)
  9. }
  10. return varName
  11. }
  12. function replaceVariablesInString(str, context) {
  13. if (typeof str !== 'string') return str
  14. const replacer = (match, varName) => {
  15. const varValue = context[varName]
  16. if (varValue === undefined || varValue === null || varValue === '') return ''
  17. if (varValue === 'undefined' || varValue === 'null') return ''
  18. if (typeof varValue === 'string') {
  19. try {
  20. const parsed = JSON.parse(varValue)
  21. if (Array.isArray(parsed)) return parsed.length === 0 ? '' : varValue
  22. } catch (e) {}
  23. }
  24. if (Array.isArray(varValue) || (typeof varValue === 'object' && varValue !== null)) {
  25. try {
  26. return JSON.stringify(varValue)
  27. } catch (e) {
  28. return String(varValue)
  29. }
  30. }
  31. return String(varValue)
  32. }
  33. const doubleBracePattern = /\{\{([\w-]+)\}\}/g
  34. const singleBracePattern = /\{([\w-]+)\}/g
  35. return str.replace(doubleBracePattern, replacer).replace(singleBracePattern, replacer)
  36. }
  37. function resolveValue(value, context) {
  38. if (typeof value === 'string' && value.startsWith('{') && value.endsWith('}')) {
  39. const varName = value.slice(1, -1)
  40. return context[varName] !== undefined ? context[varName] : value
  41. }
  42. if (Array.isArray(value)) {
  43. return value.map(item => resolveValue(item, context))
  44. }
  45. if (typeof value === 'object' && value !== null) {
  46. const resolved = {}
  47. for (const key in value) {
  48. resolved[key] = resolveValue(value[key], context)
  49. }
  50. return resolved
  51. }
  52. return value
  53. }
  54. function parseTimeString(timeStr) {
  55. if (!timeStr || typeof timeStr !== 'string' || timeStr.trim() === '') return null
  56. try {
  57. const parts = timeStr.trim().split(' ')
  58. if (parts.length !== 2) return null
  59. const datePart = parts[0].split('/')
  60. const timePart = parts[1].split(':')
  61. if (datePart.length !== 3 || timePart.length !== 2) return null
  62. const date = new Date(
  63. parseInt(datePart[0], 10),
  64. parseInt(datePart[1], 10) - 1,
  65. parseInt(datePart[2], 10),
  66. parseInt(timePart[0], 10),
  67. parseInt(timePart[1], 10),
  68. 0, 0
  69. )
  70. return isNaN(date.getTime()) ? null : date
  71. } catch (e) {
  72. return null
  73. }
  74. }
  75. // 时长单位(毫秒):ms/s/m/h/d/w/mon/月/y;组合如 1y3mon、1h30m、2d
  76. const DELAY_UNIT_MS = {
  77. ms: 1,
  78. s: 1000,
  79. m: 60 * 1000,
  80. h: 60 * 60 * 1000,
  81. d: 24 * 60 * 60 * 1000,
  82. w: 7 * 24 * 60 * 60 * 1000,
  83. mon: 30 * 24 * 60 * 60 * 1000,
  84. '月': 30 * 24 * 60 * 60 * 1000,
  85. y: 365 * 24 * 60 * 60 * 1000,
  86. }
  87. /** 解析时长字符串。单位:ms(毫秒) s(秒) m(分) h(时) d(天) w(周) mon/月(月) y(年),可组合如 1y3mon、7s、1h30m。纯数字按秒处理。 */
  88. function parseDelayString(delayStr) {
  89. if (delayStr == null) return 0
  90. if (typeof delayStr === 'number' && !Number.isNaN(delayStr) && delayStr >= 0) return Math.round(delayStr * 1000)
  91. if (typeof delayStr !== 'string' || delayStr.trim() === '') return 0
  92. try {
  93. const trimmed = String(delayStr).trim()
  94. // 单位顺序:ms、mon 必须在 m、s 前,避免被拆成 m+s
  95. const re = /(\d+)\s*(ms|mon|s|m|h|d|w|月|y)/gi
  96. let total = 0
  97. let hadMatch = false
  98. let match
  99. while ((match = re.exec(trimmed)) !== null) {
  100. const value = parseInt(match[1], 10)
  101. const unit = match[2].toLowerCase()
  102. if (value < 0 || !DELAY_UNIT_MS[unit]) continue
  103. total += value * DELAY_UNIT_MS[unit]
  104. hadMatch = true
  105. }
  106. if (hadMatch) return total
  107. // 纯数字视为秒(如 "15" 或变量解析后只剩数字)
  108. const bare = /^\d+$/.test(trimmed)
  109. if (bare) return parseInt(trimmed, 10) * 1000
  110. return 0
  111. } catch (e) {
  112. return 0
  113. }
  114. }
  115. function calculateWaitTime(data, delay) {
  116. return 0
  117. }
  118. function parseValue(str) {
  119. if (typeof str !== 'string') return str
  120. str = str.trim()
  121. if (str === 'true') return true
  122. if (str === 'false') return false
  123. if ((str.startsWith('"') && str.endsWith('"')) || (str.startsWith("'") && str.endsWith("'"))) {
  124. return str.slice(1, -1).replace(/\\"/g, '"').replace(/\\'/g, "'")
  125. }
  126. if (/^-?\d+(\.\d+)?$/.test(str)) return parseFloat(str)
  127. if (str === 'undefined') return undefined
  128. return str
  129. }
  130. function parse (action, parseContext) {
  131. const path = parseContext.actionPath || 'set'
  132. assertStrictKeys(action, ['type', 'variable', 'value'], path)
  133. if (action.variable === undefined || action.variable === null || action.variable === '') {
  134. throw new Error(`${path}: set 须提供 variable`)
  135. }
  136. const variableContext = parseContext.variableContext || {}
  137. return {
  138. type: 'set',
  139. variable: action.variable,
  140. value: resolveValue(action.value, variableContext),
  141. }
  142. }
  143. async function execute(action, ctx) {
  144. const { variableContext, evaluateExpression } = ctx
  145. if (!action.variable) return { success: true }
  146. const varName = extractVarName(action.variable)
  147. let finalValue = action.value
  148. if (typeof action.value === 'string' && /[+\-*/]/.test(action.value)) {
  149. finalValue = evaluateExpression(action.value, variableContext)
  150. } else if (typeof action.value === 'string') {
  151. const varPattern = /\{([\w-]+)\}/g
  152. let hasVariables = false
  153. finalValue = action.value.replace(varPattern, (match, vn) => {
  154. hasVariables = true
  155. const val = variableContext[vn]
  156. return val === undefined || val === null ? match : String(val)
  157. })
  158. if (!hasVariables) finalValue = resolveValue(action.value, variableContext)
  159. } else {
  160. finalValue = resolveValue(action.value, variableContext)
  161. }
  162. variableContext[varName] = finalValue
  163. return { success: true }
  164. }
  165. module.exports = {
  166. types,
  167. parse,
  168. execute,
  169. extractVarName,
  170. replaceVariablesInString,
  171. resolveValue,
  172. parseTimeString,
  173. parseDelayString,
  174. calculateWaitTime,
  175. parseValue,
  176. }