feat: add indexes and optimize list performance

This commit is contained in:
aslost
2026-08-25 19:26:20 +08:00
parent 7c7c077744
commit c31d88228e
19 changed files with 233 additions and 124 deletions
+1 -34
View File
@@ -90,7 +90,7 @@
</slot>
</span>
</span>
<span class="email-content">{{ item.formatText || '\u200B' }}</span>
<span class="email-content">{{ item.text || '\u200B' }}</span>
</div>
<div class="user-info" v-if="showUserInfo">
<div class="user">
@@ -554,37 +554,6 @@ const accountShow = computed(() => {
return uiStore.accountShow && settingStore.settings.manyEmail === 0
})
function htmlToText(email) {
if (email.content) {
const tempDiv = document.createElement('div');
tempDiv.innerHTML = email.content.replace(
/<(img|iframe|object|embed|video|audio|source|link)[^>]*>/gi, ''
);
const scriptsAndStyles = tempDiv.querySelectorAll('script, style, title');
scriptsAndStyles.forEach(el => el.remove());
let text = tempDiv.textContent || tempDiv.innerText || '';
text = text.replace(/\s+/g, ' ').trim();
return cleanSpace(text)
}
if (email.text) {
return cleanSpace(email.text)
} else {
return ''
}
}
function cleanSpace(text) {
return text
.replace(/[\u200B-\u200F\uFEFF\u034F\u200B-\u200F\u00A0\u3000\u00AD]/g, '')// 移除零宽空格
.replace(/\s+/g, ' ') // 多空白合并成一个空格
.trim();
}
function starChange(email) {
if (!email.isStar) {
@@ -734,7 +703,6 @@ function addItem(email) {
return false;
}
email.formatText = htmlToText(email);
email.formatCreateTime = fromNow(email.formatCreateTime);
if (props.timeSort) {
@@ -888,7 +856,6 @@ function getEmailList(refresh = false) {
function handleList(list) {
list.forEach(email => {
email.formatText = htmlToText(email)
email.formatCreateTime = fromNow(email.createTime);
email.test = t('received')
const statusIconMap = {
+2 -2
View File
@@ -1,7 +1,7 @@
import http from '@/axios/index.js';
export function emailList(accountId, allReceive, emailId, timeSort, size, type) {
return http.get('/email/list', {params: {accountId, allReceive, emailId, timeSort, size, type}})
export function emailList(accountId, allReceive, emailId, timeSort, size, type, full) {
return http.get('/email/list', {params: {accountId, allReceive, emailId, timeSort, size, type, full}})
}
export function emailDelete(emailIds) {
+2 -2
View File
@@ -8,6 +8,6 @@ export function starCancel(emailId) {
return http.delete('/star/cancel', {params: {emailId}})
}
export function starList(emailId,size) {
return http.get('/star/list', {params: {emailId,size}})
export function starList(emailId,size,full) {
return http.get('/star/list', {params: {emailId,size,full}})
}
+38
View File
@@ -15,8 +15,46 @@ export const useEmailStore = defineStore('email', {
showUnread: false
},
sendScroll: null,
detailMap: {},
}),
persist: {
pick: ['contentData'],
},
actions: {
fetchList(request) {
request(1).then(data => {
const list = Array.isArray(data) ? data : data?.list
this.applyFullList(list)
}).catch(e => {
console.error(e)
})
return request(0)
},
applyFullList(list) {
if (!list?.length) return
const currentId = this.contentData.email?.emailId
for (const item of list) {
if (!item?.emailId) continue
if (!item.attList) item.attList = []
this.detailMap[item.emailId] = item
if (currentId && item.emailId === currentId) {
this.contentData.email = item
}
}
},
toContentEmail(email) {
const id = email?.emailId
if (id && this.detailMap[id]) {
return this.detailMap[id]
}
return {
...email,
emailId: id || 0,
content: '',
text: '',
attList: [],
recipient: email?.recipient || '[]',
}
},
},
})
+2 -2
View File
@@ -282,7 +282,7 @@ function typeSelectChange() {
}
function jumpContent(email) {
emailStore.contentData.email = email
emailStore.contentData.email = emailStore.toContentEmail(email)
emailStore.contentData.delType = 'physics'
emailStore.contentData.showStar = false
emailStore.contentData.showReply = false
@@ -291,7 +291,7 @@ function jumpContent(email) {
function getEmailList(emailId, size) {
return allEmailList({emailId, size, ...params})
return emailStore.fetchList(full => allEmailList({emailId, size, full, ...params}))
}
async function latest() {
+33 -26
View File
@@ -34,11 +34,11 @@
<el-alert v-if="email.status === 4" :closable="false" :title="$t('complained')" class="email-msg" type="warning" show-icon />
<el-alert v-if="email.status === 5" :closable="false" :title="$t('delayed')" class="email-msg" type="warning" show-icon />
</div>
<el-scrollbar class="htm-scrollbar" :class="email.attList.length === 0 ? 'bottom-distance' : ''">
<el-scrollbar class="htm-scrollbar" :class="!email.attList?.length ? 'bottom-distance' : ''">
<ShadowHtml class="shadow-html" :html="formatImage(email.content)" v-if="email.content" />
<pre v-else class="email-text" >{{email.text}}</pre>
</el-scrollbar>
<div class="att" v-if="email.attList.length > 0">
<div class="att" v-if="email.attList?.length > 0">
<div class="att-title">
<span>{{$t('attachments')}}</span>
<span>{{$t('attCount',{total: email.attList.length})}}</span>
@@ -75,7 +75,7 @@
</template>
<script setup>
import ShadowHtml from '@/components/shadow-html/index.vue'
import {reactive, ref, watch, onMounted, onUnmounted} from "vue";
import {computed, reactive, ref, watch, onMounted, onUnmounted} from "vue";
import {useRouter} from 'vue-router'
import {ElMessage, ElMessageBox} from 'element-plus'
import {emailDelete, emailRead} from "@/request/email.js";
@@ -98,7 +98,13 @@ const settingStore = useSettingStore();
const accountStore = useAccountStore();
const emailStore = useEmailStore();
const router = useRouter()
const email = emailStore.contentData.email
const email = computed(() => emailStore.contentData.email || {
emailId: 0,
attList: [],
content: '',
text: '',
recipient: '[]',
})
const showPreview = ref(false)
const srcList = reactive([])
@@ -108,9 +114,9 @@ watch(() => accountStore.currentAccountId, () => {
})
onMounted(() => {
if (emailStore.contentData.showUnread && email.unread === EmailUnreadEnum.UNREAD) {
email.unread = EmailUnreadEnum.READ;
emailRead([email.emailId]);
if (emailStore.contentData.showUnread && email.value.unread === EmailUnreadEnum.UNREAD && email.value.emailId) {
email.value.unread = EmailUnreadEnum.READ;
emailRead([email.value.emailId]);
}
window.addEventListener('keydown', handleKeyDown);
})
@@ -130,11 +136,11 @@ function handleKeyDown(event) {
}
function openReply() {
uiStore.writerRef.openReply(email)
uiStore.writerRef.openReply(email.value)
}
function openForward() {
uiStore.writerRef.openForward(email)
uiStore.writerRef.openForward(email.value)
}
function toMessage(message) {
@@ -160,32 +166,33 @@ function isImage(filename) {
}
function formateReceive(recipient) {
if (!recipient) return ''
recipient = JSON.parse(recipient)
return recipient.map(item => item.address).join(', ')
}
function changeStar() {
if (email.isStar) {
email.isStar = 0;
starCancel(email.emailId).then(() => {
email.isStar = 0;
emailStore.cancelStarEmailId = email.emailId
if (email.value.isStar) {
email.value.isStar = 0;
starCancel(email.value.emailId).then(() => {
email.value.isStar = 0;
emailStore.cancelStarEmailId = email.value.emailId
setTimeout(() => emailStore.cancelStarEmailId = 0)
emailStore.starScroll?.deleteEmail([email.emailId])
emailStore.starScroll?.deleteEmail([email.value.emailId])
}).catch((e) => {
console.error(e)
email.isStar = 1;
email.value.isStar = 1;
})
} else {
email.isStar = 1;
starAdd(email.emailId).then(() => {
email.isStar = 1;
emailStore.addStarEmailId = email.emailId
email.value.isStar = 1;
starAdd(email.value.emailId).then(() => {
email.value.isStar = 1;
emailStore.addStarEmailId = email.value.emailId
setTimeout(() => emailStore.addStarEmailId = 0)
emailStore.starScroll?.addItem(email)
emailStore.starScroll?.addItem(email.value)
}).catch((e) => {
console.error(e)
email.isStar = 0;
email.value.isStar = 0;
})
}
}
@@ -201,23 +208,23 @@ const handleDelete = () => {
type: 'warning'
}).then(() => {
if (emailStore.contentData.delType === 'logic') {
emailDelete(email.emailId).then(() => {
emailDelete(email.value.emailId).then(() => {
ElMessage({
message: t('delSuccessMsg'),
type: 'success',
plain: true,
})
emailStore.deleteIds = [email.emailId]
emailStore.deleteIds = [email.value.emailId]
})
} else {
allEmailDelete(email.emailId).then(() => {
allEmailDelete(email.value.emailId).then(() => {
ElMessage({
message: t('delSuccessMsg'),
type: 'success',
plain: true,
})
emailStore.deleteIds = [email.emailId]
emailStore.deleteIds = [email.value.emailId]
})
}
+4 -2
View File
@@ -64,7 +64,7 @@ function changeTimeSort() {
}
function jumpContent(email) {
emailStore.contentData.email = email
emailStore.contentData.email = emailStore.toContentEmail(email)
emailStore.contentData.delType = 'logic'
emailStore.contentData.showUnread = true
emailStore.contentData.showStar = true
@@ -141,7 +141,9 @@ function cancelStar(email) {
function getEmailList(emailId, size) {
const accountId = accountStore.currentAccountId;
const allReceive = accountStore.currentAccount.allReceive;
return emailList(accountId, allReceive, emailId, params.timeSort, size, 0).then(data => {
return emailStore.fetchList(full =>
emailList(accountId, allReceive, emailId, params.timeSort, size, 0, full)
).then(data => {
data.latestEmail.reqAccountId = accountId;
data.latestEmail.allReceive = allReceive;
return data;
+4 -2
View File
@@ -56,7 +56,7 @@ function changeTimeSort() {
}
function jumpContent(email) {
emailStore.contentData.email = email
emailStore.contentData.email = emailStore.toContentEmail(email)
emailStore.contentData.delType = 'logic'
emailStore.contentData.showStar = true
emailStore.contentData.showReply = true
@@ -74,7 +74,9 @@ function cancelStar(email) {
function getEmailList(emailId, size) {
const accountId = accountStore.currentAccountId;
const allReceive = accountStore.currentAccount.allReceive;
return emailList(accountId, allReceive, emailId, params.timeSort, size, 1).then(data => {
return emailStore.fetchList(full =>
emailList(accountId, allReceive, emailId, params.timeSort, size, 1, full)
).then(data => {
data.latestEmail.reqAccountId = accountId;
data.latestEmail.allReceive = allReceive;
return data;
+6 -2
View File
@@ -2,7 +2,7 @@
<emailScroll type="star" ref="scroll"
:allow-star="false"
:cancel-success="cancelStar"
:getEmailList="starList"
:getEmailList="getEmailList"
:emailDelete="emailDelete"
:star-add="starAdd"
:star-cancel="starCancel"
@@ -28,13 +28,17 @@ const scroll = ref({})
const emailStore = useEmailStore();
function jumpContent(email) {
emailStore.contentData.email = email
emailStore.contentData.email = emailStore.toContentEmail(email)
emailStore.contentData.delType = 'logic'
emailStore.contentData.showStar = true
emailStore.contentData.showReply = true
router.push('/mail')
}
function getEmailList(emailId, size) {
return emailStore.fetchList(full => starList(emailId, size, full))
}
function cancelStar(email) {
emailStore.cancelStarEmailId = email.emailId
scroll.value.deleteEmail([email.emailId])
+1 -3
View File
@@ -1,5 +1,3 @@
import { emailConst } from '../const/entity-const';
const analysisDao = {
async numberCount(c) {
const { results } = await c.env.db.prepare(`
@@ -26,7 +24,7 @@ const analysisDao = {
SUM(CASE WHEN type = 0 AND is_del = 0 THEN 1 ELSE 0 END) AS normalReceiveTotal,
SUM(CASE WHEN type = 1 AND is_del = 0 THEN 1 ELSE 0 END) AS normalSendTotal
FROM
email where status != ${emailConst.status.SAVING}
email
) e
CROSS JOIN (
SELECT
+14
View File
@@ -55,6 +55,20 @@ const dbInit = {
} catch (e) {
console.warn(`跳过字段:${e.message}`);
}
try {
await c.env.db.batch([
c.env.db.prepare(`CREATE INDEX IF NOT EXISTS idx_email_name_nocase ON email(name COLLATE NOCASE)`),
c.env.db.prepare(`CREATE INDEX IF NOT EXISTS idx_email_subject_nocase ON email(subject COLLATE NOCASE)`),
c.env.db.prepare(`CREATE INDEX IF NOT EXISTS idx_user_email_nocase ON user(email COLLATE NOCASE)`),
c.env.db.prepare(`CREATE INDEX IF NOT EXISTS idx_email_to_email_nocase ON email(to_email COLLATE NOCASE)`),
c.env.db.prepare(`CREATE INDEX IF NOT EXISTS idx_email_send_email_nocase ON email(send_email COLLATE NOCASE)`),
c.env.db.prepare(`CREATE INDEX IF NOT EXISTS idx_email_noone_id ON email(email_id) WHERE status = 7`),
c.env.db.prepare(`CREATE INDEX IF NOT EXISTS idx_email_type_id ON email(type, email_id)`)
]);
} catch (e) {
console.warn(`跳过索引:${e.message}`);
}
},
async v3_1DB(c) {
+38
View File
@@ -0,0 +1,38 @@
import { getTableColumns, sql } from 'drizzle-orm';
import email from '../entity/email';
export const EMAIL_LIST_TEXT_LEN = 300;
/** 去掉换行/回车/制表符,并压缩连续空格、标签间空白 */
function sqlStripWhitespace(column) {
return sql`trim(replace(replace(replace(replace(replace(replace(
coalesce(${column}, ''),
char(13), ''),
char(10), ''),
char(9), ' '),
' ', ' '),
' ', ' '),
'> <', '><'))`;
}
/** 完整查询:全部字段 */
export const emailListColumns = getTableColumns(email);
/** 摘要查询:列表 + 详情头部;有 text 则不读 content,没有才查 content(去空白),响应里不返回 content */
export const emailBriefColumns = {
emailId: email.emailId,
sendEmail: email.sendEmail,
name: email.name,
subject: email.subject,
code: email.code,
recipient: email.recipient,
toEmail: email.toEmail,
type: email.type,
status: email.status,
message: email.message,
unread: email.unread,
createTime: email.createTime,
isDel: email.isDel,
content: sql`CASE WHEN trim(coalesce(${email.text}, '')) != '' THEN NULL ELSE ${sqlStripWhitespace(email.content)} END`.as('content'),
text: sql`substr(coalesce(${email.text}, ''), 1, ${EMAIL_LIST_TEXT_LEN})`.as('text'),
};
+62 -34
View File
@@ -1,5 +1,6 @@
import orm from '../entity/orm';
import email from '../entity/email';
import { emailListColumns, emailBriefColumns, EMAIL_LIST_TEXT_LEN } from '../lib/email-list-columns';
import { attConst, emailConst, isDel, settingConst } from '../const/entity-const';
import { and, desc, eq, gt, inArray, lt, count, asc, sql, ne, or, like, lte, gte } from 'drizzle-orm';
import { star } from '../entity/star';
@@ -27,13 +28,14 @@ const emailService = {
async list(c, params, userId) {
let { emailId, type, accountId, size, timeSort, allReceive } = params;
let { emailId, type, accountId, size, timeSort, allReceive, full } = params;
size = Number(size);
emailId = Number(emailId) || 0;
timeSort = Number(timeSort);
accountId = Number(accountId);
allReceive = Number(allReceive);
full = Number(full) === 1;
if (size > 50) {
size = 50;
@@ -46,10 +48,11 @@ const emailService = {
const filters = this.emailListFilters({ userId, accountId, type, allReceive, emailId, timeSort });
const countFilters = this.emailListFilters({ userId, accountId, type, allReceive, withCursor: false });
const columns = full ? emailListColumns : emailBriefColumns;
const query = orm(c)
.select({
...email,
...columns,
starId: star.starId
})
.from(email)
@@ -82,7 +85,11 @@ const emailService = {
.where(and(...countFilters))
.get();
const latestEmailQuery = orm(c).select().from(email).where(
const latestEmailQuery = orm(c).select({
emailId: email.emailId,
accountId: email.accountId,
userId: email.userId,
}).from(email).where(
and(
eq(email.userId, userId),
eq(email.type, type),
@@ -98,8 +105,11 @@ const emailService = {
isStar: item.starId != null ? 1 : 0
}));
await this.emailAddAtt(c, list);
if (full) {
await this.emailAddAtt(c, list);
} else {
this.applyListText(list);
}
if (!latestEmail) {
latestEmail = {
@@ -112,6 +122,19 @@ const emailService = {
return { list, total: totalRow.total, latestEmail };
},
toListText(item) {
const raw = emailUtils.formatText(item.text) || emailUtils.htmlToText(item.content);
return raw.replace(/\s+/g, ' ').trim().slice(0, EMAIL_LIST_TEXT_LEN);
},
applyListText(list) {
for (const item of list) {
item.text = this.toListText(item);
delete item.content;
}
return list;
},
emailListFilters({ userId, accountId, type, allReceive, emailId, timeSort, withCursor = true }) {
const conditions = [
eq(email.userId, userId),
@@ -148,28 +171,26 @@ const emailService = {
}
if (userEmail) {
conditions.push(sql`${user.email} COLLATE NOCASE LIKE ${'%' + userEmail + '%'}`);
conditions.push(sql`${user.email} COLLATE NOCASE LIKE ${userEmail + '%'}`);
}
if (accountEmail) {
conditions.push(
or(
sql`${email.toEmail} COLLATE NOCASE LIKE ${'%' + accountEmail + '%'}`,
sql`${email.sendEmail} COLLATE NOCASE LIKE ${'%' + accountEmail + '%'}`,
sql`${email.toEmail} COLLATE NOCASE LIKE ${accountEmail + '%'}`,
sql`${email.sendEmail} COLLATE NOCASE LIKE ${accountEmail + '%'}`,
)
);
}
if (name) {
conditions.push(sql`${email.name} COLLATE NOCASE LIKE ${'%' + name + '%'}`);
conditions.push(sql`${email.name} COLLATE NOCASE LIKE ${name + '%'}`);
}
if (subject) {
conditions.push(sql`${email.subject} COLLATE NOCASE LIKE ${'%' + subject + '%'}`);
conditions.push(sql`${email.subject} COLLATE NOCASE LIKE ${subject + '%'}`);
}
conditions.push(ne(email.status, emailConst.status.SAVING));
if (withCursor && emailId) {
conditions.push(timeSort ? gt(email.emailId, emailId) : lt(email.emailId, emailId));
}
@@ -792,7 +813,7 @@ const emailService = {
allReceive = accountRow.allReceive;
}
let list = await orm(c).select({...email}).from(email)
let list = await orm(c).select({ ...emailBriefColumns }).from(email)
.innerJoin(
account,
eq(account.accountId, email.accountId)
@@ -809,9 +830,7 @@ const emailService = {
.orderBy(desc(email.emailId))
.limit(20);
await this.emailAddAtt(c, list);
return list;
return this.applyListText(list);
},
async physicsDelete(c, params) {
@@ -854,12 +873,13 @@ const emailService = {
async allList(c, params) {
let { emailId, size, name, subject, accountEmail, userEmail, type, timeSort } = params;
let { emailId, size, name, subject, accountEmail, userEmail, type, timeSort, full } = params;
size = Number(size);
emailId = Number(emailId) || 0;
timeSort = Number(timeSort);
full = Number(full) === 1;
if (size > 50) {
size = 50;
@@ -867,16 +887,22 @@ const emailService = {
const filters = this.allEmailListFilters({ emailId, name, subject, accountEmail, userEmail, type, timeSort });
const countFilters = this.allEmailListFilters({ emailId, name, subject, accountEmail, userEmail, type, timeSort, withCursor: false });
const columns = full ? emailListColumns : emailBriefColumns;
const query = orm(c).select({ ...email, userEmail: user.email })
const query = orm(c).select({ ...columns, userEmail: user.email })
.from(email)
.leftJoin(user, eq(email.userId, user.userId))
.where(and(...filters));
const queryCount = orm(c).select({ total: count() })
.from(email)
.leftJoin(user, eq(email.userId, user.userId))
.where(and(...countFilters));
// count 不搜用户时无需 join user
const queryCount = userEmail
? orm(c).select({ total: count() })
.from(email)
.leftJoin(user, eq(email.userId, user.userId))
.where(and(...countFilters))
: orm(c).select({ total: count() })
.from(email)
.where(and(...countFilters));
if (timeSort) {
query.orderBy(asc(email.emailId));
@@ -886,16 +912,21 @@ const emailService = {
const listQuery = query.limit(size).all();
const totalQuery = queryCount.get();
const latestEmailQuery = orm(c).select().from(email)
.where(and(
eq(email.type, emailConst.type.RECEIVE),
ne(email.status, emailConst.status.SAVING)
))
const latestEmailQuery = orm(c).select({
emailId: email.emailId,
accountId: email.accountId,
userId: email.userId,
}).from(email)
.where(eq(email.type, emailConst.type.RECEIVE))
.orderBy(desc(email.emailId)).limit(1).get();
let [list, totalRow, latestEmail] = await Promise.all([listQuery, totalQuery, latestEmailQuery]);
await this.emailAddAtt(c, list);
if (full) {
await this.emailAddAtt(c, list);
} else {
this.applyListText(list);
}
if (!latestEmail) {
latestEmail = {
@@ -912,20 +943,17 @@ const emailService = {
const { emailId } = params;
let list = await orm(c).select({...email, userEmail: user.email}).from(email)
let list = await orm(c).select({ ...emailBriefColumns, userEmail: user.email }).from(email)
.leftJoin(user, eq(email.userId, user.userId))
.where(
and(
gt(email.emailId, emailId),
eq(email.type, emailConst.type.RECEIVE),
ne(email.status, emailConst.status.SAVING)
eq(email.type, emailConst.type.RECEIVE)
))
.orderBy(desc(email.emailId))
.limit(20);
await this.emailAddAtt(c, list);
return list;
return this.applyListText(list);
},
async emailAddAtt(c, list) {
+18 -11
View File
@@ -4,6 +4,7 @@ import emailService from './email-service';
import BizError from '../error/biz-error';
import { and, desc, eq, lt, sql, inArray } from 'drizzle-orm';
import email from '../entity/email';
import { emailListColumns, emailBriefColumns } from '../lib/email-list-columns';
import { isDel } from '../const/entity-const';
import attService from "./att-service";
import { t } from '../i18n/i18n'
@@ -41,14 +42,16 @@ const starService = {
},
async list(c, params, userId) {
let { emailId, size } = params;
let { emailId, size, full } = params;
emailId = Number(emailId) || 0;
size = Number(size);
full = Number(full) === 1;
const columns = full ? emailListColumns : emailBriefColumns;
const list = await orm(c).select({
isStar: sql`1`.as('isStar'),
starId: star.starId
, ...email
starId: star.starId,
...columns
}).from(star)
.leftJoin(email, eq(email.emailId, star.emailId))
.where(
@@ -60,14 +63,18 @@ const starService = {
.limit(size)
.all();
const emailIds = list.map(item => item.emailId);
const attsList = await attService.selectByEmailIds(c, emailIds);
list.forEach(emailRow => {
const atts = attsList.filter(attsRow => attsRow.emailId === emailRow.emailId);
emailRow.attList = atts;
});
if (full) {
const emailIds = list.map(item => item.emailId);
const attsList = await attService.selectByEmailIds(c, emailIds);
list.forEach(emailRow => {
emailRow.attList = attsList.filter(attsRow => attsRow.emailId === emailRow.emailId);
});
} else {
list.forEach(emailRow => {
emailRow.text = emailService.toListText(emailRow);
delete emailRow.content;
});
}
return { list };
},
+4
View File
@@ -350,6 +350,10 @@ const userService = {
},
async resetDaySendCount(c) {
// 仅 UTC 0 点执行,便于配合每小时 cron
if (new Date().getUTCHours() !== 0) {
return;
}
const roleList = await roleService.selectByIdsAndSendType(c, 'email:send', roleConst.sendType.DAY);
const roleIds = roleList.map(action => action.roleId);
await orm(c).update(user).set({ sendCount: 0 }).where(inArray(user.type, roleIds)).run();
+1 -1
View File
@@ -32,7 +32,7 @@ not_found_handling = "single-page-application"
run_worker_first = true
[triggers]
crons = ["0 16 * * *"]
crons = ["0 * * * *"] #每小时执行;resetDaySendCount 仅在 UTC 0 点生效
[vars]
+1 -1
View File
@@ -38,7 +38,7 @@ run_worker_first = true
[vars]
ai_model = ""
analysis_cache = false
orm_log = false
orm_log = true
domain = ["example.com", "example2.com", "example3.com", "example4.com"]
admin = "[email protected]"
jwt_secret = "b7f29a1d-18e2-4d3b-941f-f6b2c97c02fd"
+1 -1
View File
@@ -30,7 +30,7 @@ not_found_handling = "single-page-application"
run_worker_first = true
[triggers]
crons = ["0 16 * * *"] #每天晚上12点执行每日任务,刷新分析缓存
crons = ["0 * * * *"] #每小时执行;resetDaySendCount 仅在 UTC 0 点生效
[vars]
+1 -1
View File
@@ -32,7 +32,7 @@ not_found_handling = "single-page-application"
run_worker_first = true
[triggers]
crons = ["0 16 * * *"] #每天晚上12点执行每日任务,刷新分析缓存
crons = ["0 * * * *"] #每小时执行;resetDaySendCount 仅在 UTC 0 点生效
[vars]