| 1 | /* |
| 2 | * @Author: nevin |
| 3 | * @Date: 2025-01-24 17:10:35 |
| 4 | * @LastEditors: nevin |
| 5 | * @Description: autoRun AutoRun |
| 6 | */ |
| 7 | import { Injectable } from '../core/decorators'; |
| 8 | import { |
| 9 | AutoRunModel, |
| 10 | AutoRunStatus, |
| 11 | AutoRunType, |
| 12 | } from '../../db/models/autoRun'; |
| 13 | import { |
| 14 | AutoRunRecordModel, |
| 15 | AutoRunRecordStatus, |
| 16 | } from '../../db/models/autoRunRecord'; |
| 17 | import { FindOptionsWhere, MoreThan, Not, Repository } from 'typeorm'; |
| 18 | import { AppDataSource } from '../../db'; |
| 19 | import windowOperate from '../../util/windowOperate'; |
| 20 | import { SendChannelEnum } from '../../../commont/UtilsEnum'; |
| 21 | import { hasTriggered, parseCycleType } from './comment'; |
| 22 | |
| 23 | @Injectable() |
| 24 | export class AutoRunService { |
| 25 | private autoRunRepository: Repository<AutoRunModel>; |
| 26 | private autoRunRecordRepository: Repository<AutoRunRecordModel>; |
| 27 | |
| 28 | constructor() { |
| 29 | this.autoRunRepository = AppDataSource.getRepository(AutoRunModel); |
| 30 | this.autoRunRecordRepository = |
| 31 | AppDataSource.getRepository(AutoRunRecordModel); |
| 32 | } |
| 33 | |
| 34 | // 限制处于启动状态的任务数量 |
| 35 | private async chectAutoRunCount(userId: string): Promise<boolean> { |
| 36 | const count = await this.autoRunRepository.count({ |
| 37 | where: { |
| 38 | userId, |
| 39 | status: AutoRunStatus.DOING, |
| 40 | }, |
| 41 | }); |
| 42 | |
| 43 | if (count >= 100) return false; |
| 44 | |
| 45 | return true; |
| 46 | } |
| 47 | |
| 48 | // 创建进程 |
| 49 | async createAutoRun(info: Partial<AutoRunModel>, data: Record<string, any>) { |
| 50 | if (!(await this.chectAutoRunCount(data.userId!))) return null; |
| 51 | |
| 52 | info.data = JSON.stringify(data); |
| 53 | return await this.autoRunRepository.save(info); |
| 54 | } |
| 55 | |
| 56 | // 根据ID查询进程信息 |
| 57 | async findAutoRunById(id: number) { |
| 58 | const info = await this.autoRunRepository.findOne({ |
| 59 | where: { |
| 60 | id, |
| 61 | }, |
| 62 | }); |
| 63 | |
| 64 | if (info) info.dataInfo = JSON.parse(info.data); |
| 65 | |
| 66 | return info; |
| 67 | } |
| 68 | |
| 69 | // 查询进程列表 |
| 70 | async findAutoRunList( |
| 71 | pageInfo: { |
| 72 | page: number; |
| 73 | pageSize: number; |
| 74 | }, |
| 75 | query: { |
| 76 | type?: AutoRunType; |
| 77 | status?: AutoRunStatus; |
| 78 | cycleType?: string; |
| 79 | accountId?: number; |
| 80 | dataId?: string; |
| 81 | }, |
| 82 | ): Promise<{ |
| 83 | list: AutoRunModel[]; |
| 84 | total: number; |
| 85 | }> { |
| 86 | const { page, pageSize } = pageInfo; |
| 87 | const whereClause: FindOptionsWhere<AutoRunModel> = { |
| 88 | status: Not(AutoRunStatus.DELETE), |
| 89 | ...(query.type !== undefined && { type: query.type }), |
| 90 | ...(query.status !== undefined && { status: query.status }), |
| 91 | ...(query.cycleType !== undefined && { cycleType: query.cycleType }), |
| 92 | ...(query.accountId !== undefined && { accountId: query.accountId }), |
| 93 | ...(query.dataId !== undefined && { dataId: query.dataId }), |
| 94 | }; |
| 95 | |
| 96 | const list = await this.autoRunRepository.find({ |
| 97 | where: whereClause, |
| 98 | skip: (page - 1) * pageSize, |
| 99 | take: pageSize, |
| 100 | }); |
| 101 | |
| 102 | const total = await this.autoRunRepository.count({ |
| 103 | where: whereClause, |
| 104 | }); |
| 105 | |
| 106 | return { |
| 107 | list, |
| 108 | total, |
| 109 | }; |
| 110 | } |
| 111 | |
| 112 | // 查询需要运行的进程列表 |
| 113 | async findAutoRunListOfNeedRun(userId: string) { |
| 114 | return await this.autoRunRepository.find({ |
| 115 | where: { |
| 116 | userId, |
| 117 | status: AutoRunStatus.DOING, |
| 118 | }, |
| 119 | }); |
| 120 | } |
| 121 | |
| 122 | // 更新进程状态 |
| 123 | async updateAutoRunStatus(id: number, status: AutoRunStatus) { |
| 124 | return await this.autoRunRepository.update(id, { |
| 125 | status, |
| 126 | }); |
| 127 | } |
| 128 | |
| 129 | // 创建进程记录 |
| 130 | async createAutoRunRecord(autoRun: AutoRunModel) { |
| 131 | const data = { |
| 132 | autoRunId: autoRun.id, |
| 133 | userId: autoRun.userId, |
| 134 | type: autoRun.type, |
| 135 | cycleType: autoRun.cycleType, |
| 136 | status: AutoRunRecordStatus.DOING, |
| 137 | }; |
| 138 | return await this.autoRunRecordRepository.save(data); |
| 139 | } |
| 140 | |
| 141 | // 查询进程记录列表 |
| 142 | async findAutoRunRecordList( |
| 143 | pageInfo: { |
| 144 | page: number; |
| 145 | pageSize: number; |
| 146 | }, |
| 147 | query: { |
| 148 | autoRunId: number; |
| 149 | type?: AutoRunType; |
| 150 | status?: AutoRunRecordStatus; |
| 151 | cycleType?: string; |
| 152 | }, |
| 153 | ) { |
| 154 | const { page, pageSize } = pageInfo; |
| 155 | const whereClause: FindOptionsWhere<AutoRunRecordModel> = { |
| 156 | ...(query.autoRunId !== undefined && { autoRunId: query.autoRunId }), |
| 157 | ...(query.type !== undefined && { type: query.type }), |
| 158 | ...(query.status !== undefined && { status: query.status }), |
| 159 | ...(query.cycleType !== undefined && { cycleType: query.cycleType }), |
| 160 | }; |
| 161 | |
| 162 | const list = await this.autoRunRecordRepository.find({ |
| 163 | where: whereClause, |
| 164 | skip: (page - 1) * pageSize, |
| 165 | take: pageSize, |
| 166 | }); |
| 167 | |
| 168 | const total = await this.autoRunRecordRepository.count({ |
| 169 | where: whereClause, |
| 170 | }); |
| 171 | |
| 172 | return { |
| 173 | list, |
| 174 | total, |
| 175 | }; |
| 176 | } |
| 177 | |
| 178 | // 更新进程记录状态 |
| 179 | async updateAutoRunRecordStatus(id: number, status: AutoRunRecordStatus) { |
| 180 | return await this.autoRunRecordRepository.update(id, { |
| 181 | status, |
| 182 | }); |
| 183 | } |
| 184 | |
| 185 | // 发送自动任务进度通知 |
| 186 | async sendAutoRunProgress(id: number, status: -1 | 0 | 1 | 2, error?: any) { |
| 187 | const autoRunInfo = await this.findAutoRunById(id); |
| 188 | if (!autoRunInfo) return; |
| 189 | |
| 190 | windowOperate.sendRenderMsg( |
| 191 | SendChannelEnum.AutoRun, |
| 192 | status, |
| 193 | autoRunInfo, |
| 194 | error, |
| 195 | ); |
| 196 | } |
| 197 | |
| 198 | // 查找周期内的最近一条记录 |
| 199 | async findLastAutoRunRecord( |
| 200 | autoRun: AutoRunModel, |
| 201 | cycleType: 'day' | 'week' | 'month', |
| 202 | ) { |
| 203 | const where: FindOptionsWhere<AutoRunRecordModel> = { |
| 204 | autoRunId: autoRun.id, |
| 205 | }; |
| 206 | |
| 207 | if (cycleType === 'day') { |
| 208 | where.createTime = MoreThan(new Date(new Date().setHours(0, 0, 0, 0))); |
| 209 | } else if (cycleType === 'week') { |
| 210 | where.createTime = MoreThan( |
| 211 | new Date(new Date().setDate(new Date().getDate() - 7)), |
| 212 | ); |
| 213 | } else if (cycleType === 'month') { |
| 214 | where.createTime = MoreThan( |
| 215 | new Date(new Date().setDate(new Date().getDate() - 30)), |
| 216 | ); |
| 217 | } |
| 218 | |
| 219 | const lastRecord = await this.autoRunRecordRepository.findOne({ |
| 220 | where, |
| 221 | order: { |
| 222 | createTime: 'DESC', |
| 223 | }, |
| 224 | }); |
| 225 | |
| 226 | return lastRecord; |
| 227 | } |
| 228 | |
| 229 | // 判断是否需要触发运行 |
| 230 | async isNeedAutoRunToRun(autoRun: AutoRunModel): Promise<boolean> { |
| 231 | const { cycleType } = autoRun; |
| 232 | |
| 233 | const isHasTriggered = hasTriggered(cycleType); |
| 234 | if (!isHasTriggered) return false; |
| 235 | |
| 236 | const { type } = parseCycleType(cycleType); |
| 237 | if (!type) return false; |
| 238 | |
| 239 | const record = await this.findLastAutoRunRecord(autoRun, type); |
| 240 | |
| 241 | return !record; |
| 242 | } |
| 243 | } |
| 244 |