contentSecurityCheck.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. /**
  2. * 内容安全检测工具类
  3. * 用于检测用户发布的文字、图片是否包含色情、低俗、违规等内容
  4. * 使用微信小程序内容安全API
  5. */
  6. const BASE_URL = 'https://api.zhongruanke.cn/api'
  7. // 敏感词列表(本地基础过滤)
  8. const SENSITIVE_WORDS = [
  9. // 色情相关
  10. '色情', '裸体', '性爱', '约炮', '一夜情', '援交', '卖淫', '嫖娼',
  11. // 低俗相关
  12. '傻逼', '操你', '草泥马', '妈的', '他妈的', '狗日的', '王八蛋',
  13. // 违规相关
  14. '赌博', '毒品', '枪支', '炸弹', '恐怖', '暴力',
  15. // 诈骗相关
  16. '刷单', '兼职日结', '高额返利', '免费领取'
  17. ]
  18. export default {
  19. /**
  20. * 检测文字内容是否安全
  21. * @param {string} content - 要检测的文字内容
  22. * @returns {Promise<{safe: boolean, message: string}>}
  23. */
  24. async checkText(content) {
  25. if (!content || !content.trim()) {
  26. return { safe: true, message: '' }
  27. }
  28. // 1. 本地敏感词过滤(快速检测)
  29. const localResult = this.localTextCheck(content)
  30. if (!localResult.safe) {
  31. return localResult
  32. }
  33. // 2. 调用后端接口进行内容安全检测
  34. try {
  35. const [error, res] = await uni.request({
  36. url: `${BASE_URL}/content-security/check-text`,
  37. method: 'POST',
  38. data: { content: content },
  39. timeout: 10000
  40. })
  41. if (error) {
  42. console.error('文字安全检测请求失败:', error)
  43. // 网络错误时,仅依赖本地检测结果
  44. return localResult
  45. }
  46. if (res.statusCode === 200 && res.data) {
  47. if (res.data.code === 200) {
  48. const data = res.data.data
  49. if (data && data.safe === false) {
  50. return {
  51. safe: false,
  52. message: data.message || '内容包含违规信息,请修改后重试'
  53. }
  54. }
  55. } else if (res.data.code === 87014) {
  56. // 微信内容安全API返回的违规码
  57. return {
  58. safe: false,
  59. message: '内容包含违规信息,请修改后重试'
  60. }
  61. }
  62. }
  63. return { safe: true, message: '' }
  64. } catch (e) {
  65. console.error('文字安全检测异常:', e)
  66. return localResult
  67. }
  68. },
  69. /**
  70. * 本地敏感词检测
  71. * @param {string} content - 要检测的文字内容
  72. * @returns {{safe: boolean, message: string}}
  73. */
  74. localTextCheck(content) {
  75. if (!content) return { safe: true, message: '' }
  76. const lowerContent = content.toLowerCase()
  77. for (const word of SENSITIVE_WORDS) {
  78. if (lowerContent.includes(word.toLowerCase())) {
  79. return {
  80. safe: false,
  81. message: '内容包含敏感词,请修改后重试'
  82. }
  83. }
  84. }
  85. return { safe: true, message: '' }
  86. },
  87. /**
  88. * 检测图片是否安全
  89. * @param {string} imageUrl - 图片URL(需要是可访问的网络地址)
  90. * @returns {Promise<{safe: boolean, message: string}>}
  91. */
  92. async checkImage(imageUrl) {
  93. if (!imageUrl) {
  94. return { safe: true, message: '' }
  95. }
  96. // 如果是本地临时文件,跳过检测(上传后再检测)
  97. if (imageUrl.startsWith('wxfile://') || imageUrl.startsWith('http://tmp/')) {
  98. return { safe: true, message: '' }
  99. }
  100. try {
  101. const [error, res] = await uni.request({
  102. url: `${BASE_URL}/content-security/check-image`,
  103. method: 'POST',
  104. data: { imageUrl: imageUrl },
  105. timeout: 15000
  106. })
  107. if (error) {
  108. console.error('图片安全检测请求失败:', error)
  109. return { safe: true, message: '' } // 网络错误时默认通过
  110. }
  111. if (res.statusCode === 200 && res.data) {
  112. if (res.data.code === 200) {
  113. const data = res.data.data
  114. if (data && data.safe === false) {
  115. return {
  116. safe: false,
  117. message: data.message || '图片包含违规内容,请更换后重试'
  118. }
  119. }
  120. } else if (res.data.code === 87014) {
  121. return {
  122. safe: false,
  123. message: '图片包含违规内容,请更换后重试'
  124. }
  125. }
  126. }
  127. return { safe: true, message: '' }
  128. } catch (e) {
  129. console.error('图片安全检测异常:', e)
  130. return { safe: true, message: '' }
  131. }
  132. },
  133. /**
  134. * 批量检测图片是否安全
  135. * @param {string[]} imageUrls - 图片URL数组
  136. * @returns {Promise<{safe: boolean, message: string, failedIndex: number}>}
  137. */
  138. async checkImages(imageUrls) {
  139. if (!imageUrls || imageUrls.length === 0) {
  140. return { safe: true, message: '', failedIndex: -1 }
  141. }
  142. // 过滤掉本地临时文件
  143. const networkUrls = imageUrls.filter(url =>
  144. url && !url.startsWith('wxfile://') && !url.startsWith('http://tmp/')
  145. )
  146. if (networkUrls.length === 0) {
  147. return { safe: true, message: '', failedIndex: -1 }
  148. }
  149. // 逐个检测图片
  150. for (let i = 0; i < networkUrls.length; i++) {
  151. const result = await this.checkImage(networkUrls[i])
  152. if (!result.safe) {
  153. return {
  154. safe: false,
  155. message: `第${i + 1}张图片包含违规内容,请更换后重试`,
  156. failedIndex: i
  157. }
  158. }
  159. }
  160. return { safe: true, message: '', failedIndex: -1 }
  161. },
  162. /**
  163. * 综合检测文字和图片
  164. * @param {string} content - 文字内容
  165. * @param {string[]} imageUrls - 图片URL数组
  166. * @returns {Promise<{safe: boolean, message: string}>}
  167. */
  168. async checkContent(content, imageUrls) {
  169. // 1. 检测文字
  170. const textResult = await this.checkText(content)
  171. if (!textResult.safe) {
  172. return textResult
  173. }
  174. // 2. 检测图片
  175. if (imageUrls && imageUrls.length > 0) {
  176. const imageResult = await this.checkImages(imageUrls)
  177. if (!imageResult.safe) {
  178. return imageResult
  179. }
  180. }
  181. return { safe: true, message: '' }
  182. }
  183. }