{"record":{"id":"927ced6e2ea4ebd3","repo":"yikart/AiToEarn","slug":"rating-like-unlike-927ced","errorCode":null,"errorMessage":"参数错误: rating必须为like或unlike","messagePattern":"参数错误: rating必须为like或unlike","errorType":"validation","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"project/aitoearn-electron/server/src/modules/plat/twitter/twitter.controller.ts","lineNumber":374,"sourceCode":"      },\n      required: ['accountId', 'tweetId', 'rating']\n    }\n  })\n  async rateTweet(\n    @GetToken() systemToken: TokenInfo,\n    @Body('tweetId') tweetId: string,\n    @Body('accountId') accountId: string,\n    @Body('rating') rating: string,\n  ) {\n    const userId = systemToken.id;\n    if (!tweetId || !userId || !accountId || ! rating) {\n      throw new BadRequestException('tweetId, rating和accountId是必须的');\n    }\n\n    const accessToken = await this.twitterAuthService.getUserAccessToken(accountId);\n    // 在方法开始处验证\n    if (![\"like\", \"unlike\"].includes(rating)) {\n        throw new BadRequestException('参数错误: rating必须为like或unlike');\n    }\n\n    // 后续处理\n    if (rating === \"like\") {\n        return this.twitterService.likeTweet(accessToken, userId, accountId, tweetId);\n    } else {\n        return this.twitterService.unlikeTweet(accessToken, userId, accountId, tweetId);\n    }\n  }\n\n}\n","sourceCodeStart":356,"sourceCodeEnd":386,"githubUrl":"https://github.com/yikart/AiToEarn/blob/d3aa8bea5b146a8675607cf0144d891aad3e9683/project/aitoearn-electron/server/src/modules/plat/twitter/twitter.controller.ts#L356-L386","documentation":"Thrown by rateTweet after the required-params guard passes, when the supplied rating is not exactly 'like' or 'unlike'. The endpoint maps rating to the corresponding Twitter like/unlike API call, so any other value (including different casing like 'Like') is rejected with this 400.","triggerScenarios":"POST to rateTweet with rating set to e.g. 'unLike', 'LIKE', 'favorite', a numeric flag, or undefined-adjacent whitespace string.","commonSituations":"Client uses boolean-like values (1/0, true/false) instead of the strings; legacy API version used different rating vocabulary; casing mismatch after refactoring.","solutions":["Send rating exactly as the lowercase string 'like' or 'unlike'.","Normalize the client value with toLowerCase() before sending.","Map boolean/numeric UI state to the two accepted strings at the call site.","Consider accepting an enum validated by a Nest DTO (class-validator @IsIn(['like','unlike'])) for earlier, clearer errors."],"exampleFix":"// before\nawait api.post('/twitter/tweet/rate', { tweetId, accountId, rating: isLiked });\n// after\nawait api.post('/twitter/tweet/rate', { tweetId, accountId, rating: isLiked ? 'like' : 'unlike' });","handlingStrategy":"validation","validationCode":"const RATING_VALUES = ['like', 'unlike'] as const;\ntype Rating = typeof RATING_VALUES[number];\nif (!RATING_VALUES.includes(rating as Rating)) throw new Error('rating必须为like或unlike');","typeGuard":"function isRating(v: unknown): v is 'like' | 'unlike' {\n  return v === 'like' || v === 'unlike';\n}","tryCatchPattern":"try {\n  await api.post('/twitter/tweet/rate', { tweetId, accountId, rating });\n} catch (e) {\n  if (e.response?.status === 400 && e.response.data?.message?.includes('like或unlike')) {\n    rating = rating === 'like' ? 'unlike' : 'like'; // or fix the value source\n  }\n}","preventionTips":["Use a TypeScript union type 'like' | 'unlike' at the call site.","Always send lowercase strings; normalize with toLowerCase() if the value comes from UI state.","Never send booleans/numbers as rating.","Prefer a shared enum/constant between client and server to avoid drift."],"tags":["nestjs","validation","bad-request","invalid-parameter","enum"],"backgroundTag":"invalid-parameter-value","analyzedSha":"d3aa8bea5b146a8675607cf0144d891aad3e9683","analyzedAt":"2026-08-31T14:19:24.185Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}