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 };
}
});