diff --git a/src/config/router.js b/src/config/router.js index bd5eca8..9aba426 100644 --- a/src/config/router.js +++ b/src/config/router.js @@ -39,6 +39,7 @@ module.exports = [ // 系统管理 - 短信记录 ['/admin/system/sms', 'admin/system/sms/index'], ['/admin/system/sms/list', 'admin/system/sms/list'], + ['/admin/system/sms/clearLimit', 'admin/system/sms/clearLimit', 'post'], // 患者管理 ['/admin/patient', 'admin/patient/index'], diff --git a/src/controller/admin/system/sms.js b/src/controller/admin/system/sms.js index 627eaed..02cee03 100644 --- a/src/controller/admin/system/sms.js +++ b/src/controller/admin/system/sms.js @@ -47,4 +47,34 @@ module.exports = class extends Base { return this.json({ code: 0, data: list }); } + + // 清除短信发送限制(手机号或IP) + async clearLimitAction() { + const { type, value } = this.post(); + if (!type || !value) return this.fail('参数错误'); + + const dayjs = require('dayjs'); + const today = dayjs().format('YYYY-MM-DD'); + let cleared = []; + + if (type === 'phone') { + // 清除手机号相关的所有限制 + const bizTypes = ['real_name_auth', 'change_phone', 'login']; + for (const biz of bizTypes) { + await think.cache(`sms:rate_limit:${value}:${biz}`, null); + await think.cache(`sms:daily:${value}:${today}`, null); + await think.cache(`sms:code:${value}:${biz}`, null); + } + cleared = ['频率限制', '每日上限', '验证码缓存']; + } else if (type === 'ip') { + await think.cache(`sms:ip_hour:${value}`, null); + await think.cache(`sms:ip_day:${value}:${today}`, null); + cleared = ['IP小时限制', 'IP每日限制']; + } else { + return this.fail('类型错误'); + } + + await this.log('edit', '系统管理', `清除短信限制: ${type === 'phone' ? '手机号' : 'IP'} ${value} (${cleared.join('、')})`); + return this.success(); + } }; diff --git a/view/admin/system/sms_index.html b/view/admin/system/sms_index.html index ec57b2b..6f5a48a 100644 --- a/view/admin/system/sms_index.html +++ b/view/admin/system/sms_index.html @@ -16,6 +16,7 @@ + @@ -30,6 +31,8 @@ value-format="YYYY-MM-DD" style="width:240px;" :teleported="false"> 搜索 重置 +
+ 清除短信限制 @@ -68,6 +71,25 @@ + + + + + + + 手机号 + IP地址 + + + + + + + + {% endblock %} @@ -88,6 +110,7 @@ const app = createApp({ const bizTypeMap = { real_name_auth: { type: 'primary', text: '实名认证' }, + login: { type: '', text: '登录' }, change_phone: { type: 'warning', text: '修改手机号' }, audit_approved: { type: 'success', text: '审核通过' }, audit_rejected: { type: 'danger', text: '审核驳回' } @@ -133,7 +156,33 @@ const app = createApp({ onMounted(() => loadList()); - return { loading, list, total, page, pageSize, query, dateRange, bizTypeMap, statusMap, loadList, resetQuery, onSizeChange }; + // 清除短信限制 + const showClearDialog = ref(false); + const clearLoading = ref(false); + const clearForm = reactive({ type: 'phone', value: '' }); + + async function handleClearLimit() { + if (!clearForm.value.trim()) { + return ElementPlus.ElMessage.warning('请输入' + (clearForm.type === 'phone' ? '手机号' : 'IP地址')); + } + clearLoading.value = true; + try { + const res = await fetch('/admin/system/sms/clearLimit', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ type: clearForm.type, value: clearForm.value.trim() }) + }).then(r => r.json()); + if (res.errno === 0) { + ElementPlus.ElMessage.success('已清除'); + showClearDialog.value = false; + clearForm.value = ''; + } else { + ElementPlus.ElMessage.error(res.errmsg || '操作失败'); + } + } finally { clearLoading.value = false; } + } + + return { loading, list, total, page, pageSize, query, dateRange, bizTypeMap, statusMap, loadList, resetQuery, onSizeChange, showClearDialog, clearLoading, clearForm, handleClearLimit }; } });