功能: 为企业微信添加任务队列和子任务解析。
- 在`api.server.ts`中注册`fastify-xml-body-parser`插件。 - 在`task.queue.ts`中添加`ChainQueue`类和`addTaskToQueue`方法。 - 改进`wechatwork.service.ts`和`task.service.ts`中任务解析和处理。 - 在`ChainTask.ts`中添加新属性和方法到`ChainTaskClass`中。
This commit is contained in:
parent
a1ce07f8fb
commit
ab782820fe
@ -30,6 +30,7 @@ export class ApiServer {
|
|||||||
}
|
}
|
||||||
private registerPlugins() {
|
private registerPlugins() {
|
||||||
this.server.register(require('fastify-formbody'))
|
this.server.register(require('fastify-formbody'))
|
||||||
|
this.server.register(require('fastify-xml-body-parser'))
|
||||||
this.server.register(zReqParserPlugin)
|
this.server.register(zReqParserPlugin)
|
||||||
this.server.register(helmet, { hidePoweredBy: false })
|
this.server.register(helmet, { hidePoweredBy: false })
|
||||||
this.server.register(zTokenParserPlugin)
|
this.server.register(zTokenParserPlugin)
|
||||||
|
@ -20,6 +20,10 @@ export enum TaskStatus {
|
|||||||
export class ChainTaskClass extends BaseModule {
|
export class ChainTaskClass extends BaseModule {
|
||||||
@prop({ required: true })
|
@prop({ required: true })
|
||||||
public taskId!: string
|
public taskId!: string
|
||||||
|
@prop()
|
||||||
|
public name: string
|
||||||
|
@prop()
|
||||||
|
public desc: string
|
||||||
|
|
||||||
@prop({ type: mongoose.Schema.Types.Mixed })
|
@prop({ type: mongoose.Schema.Types.Mixed })
|
||||||
public taskData: any
|
public taskData: any
|
||||||
@ -95,6 +99,32 @@ export class ChainTaskClass extends BaseModule {
|
|||||||
public static async allUnFinishedTask() {
|
public static async allUnFinishedTask() {
|
||||||
return ChainTask.find({ allEnd: false })
|
return ChainTask.find({ allEnd: false })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析企业微信审批信息
|
||||||
|
*/
|
||||||
|
public static async parseWxApprovalInfo({ taskId, name, desc, data }) {
|
||||||
|
let maxTryCount = parseInt(process.env.CHAIN_MAX_TRY)
|
||||||
|
let chainTask = await ChainTask.insertOrUpdate({ taskId }, { name, desc, data })
|
||||||
|
let subTasks: any = []
|
||||||
|
if (chainTask.newRecord) {
|
||||||
|
for (let sub of data) {
|
||||||
|
let subType = sub.type
|
||||||
|
let subTask = new RequestTask({
|
||||||
|
taskId,
|
||||||
|
chainTaskId: chainTask.id,
|
||||||
|
taskType: subType,
|
||||||
|
reqData: sub,
|
||||||
|
maxTryCount,
|
||||||
|
})
|
||||||
|
await subTask.save()
|
||||||
|
chainTask.tasks.pushOnce(subTask.id)
|
||||||
|
subTasks.push(subTask)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await chainTask.save()
|
||||||
|
return subTasks
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ChainTask = getModelForClass(ChainTaskClass, { existingConnection: ChainTaskClass['db'] })
|
export const ChainTask = getModelForClass(ChainTaskClass, { existingConnection: ChainTaskClass['db'] })
|
||||||
|
29
src/queue/task.queue.ts
Normal file
29
src/queue/task.queue.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { AsyncQueue, createAsyncQueue } from 'common/AsyncQueue'
|
||||||
|
import { singleton } from 'decorators/singleton'
|
||||||
|
import { DocumentType } from '@typegoose/typegoose'
|
||||||
|
import { ReqTaskStatus, RequestTaskClass } from 'models/RequestTask'
|
||||||
|
import { BlockChain } from 'chain/BlockChain'
|
||||||
|
import { ChainTask } from 'models/ChainTask'
|
||||||
|
import logger from 'logger/logger'
|
||||||
|
import { TaskSvr } from 'service/task.service'
|
||||||
|
|
||||||
|
@singleton
|
||||||
|
export class ChainQueue {
|
||||||
|
private queue: AsyncQueue
|
||||||
|
private blockChain: BlockChain
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.queue = createAsyncQueue()
|
||||||
|
this.blockChain = new BlockChain()
|
||||||
|
}
|
||||||
|
|
||||||
|
public async addTaskToQueue(spNo: string) {
|
||||||
|
this.queue.push(async () => {
|
||||||
|
try {
|
||||||
|
await new TaskSvr().parseOneTask(spNo)
|
||||||
|
} catch (err) {
|
||||||
|
logger.error('error add task: ' + err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,15 @@
|
|||||||
import { singleton } from 'decorators/singleton'
|
import { singleton } from 'decorators/singleton'
|
||||||
|
import { ChainTask } from 'models/ChainTask'
|
||||||
|
import { ChainQueue } from 'queue/chain.queue'
|
||||||
|
import { WechatWorkService } from './wechatwork.service'
|
||||||
|
|
||||||
@singleton
|
@singleton
|
||||||
export class TaskSvr {
|
export class TaskSvr {
|
||||||
public async parseOneTask(data: any) {}
|
public async parseOneTask(spNo: string) {
|
||||||
|
let data = await new WechatWorkService().parseOneTask(spNo)
|
||||||
|
let subTasks = await ChainTask.parseWxApprovalInfo(data)
|
||||||
|
for (let subTask of subTasks) {
|
||||||
|
new ChainQueue().addTaskToQueue(subTask)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,20 @@
|
|||||||
import axios, { AxiosRequestConfig } from 'axios'
|
import axios, { AxiosRequestConfig } from 'axios'
|
||||||
import { singleton } from 'decorators/singleton'
|
import { singleton } from 'decorators/singleton'
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
|
import os from 'os'
|
||||||
|
import path from 'path'
|
||||||
|
import { excelToJson } from 'utils/excel.util'
|
||||||
|
|
||||||
|
// 1-审批中;2-已通过;3-已驳回;4-已撤销;6-通过后撤销;7-已删除;10-已支付
|
||||||
|
export enum TaskStatus {
|
||||||
|
PEDING = 1,
|
||||||
|
PASS = 2,
|
||||||
|
REJECT = 3,
|
||||||
|
CANCEL = 4,
|
||||||
|
PASS_CANCEL = 6,
|
||||||
|
DELETE = 7,
|
||||||
|
PAY = 10,
|
||||||
|
}
|
||||||
|
|
||||||
const WX_API_HOST = 'https://qyapi.weixin.qq.com'
|
const WX_API_HOST = 'https://qyapi.weixin.qq.com'
|
||||||
@singleton
|
@singleton
|
||||||
@ -72,6 +86,42 @@ export class WechatWorkService {
|
|||||||
let response = await axios.request(config).then(response => {
|
let response = await axios.request(config).then(response => {
|
||||||
return response.data
|
return response.data
|
||||||
})
|
})
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 调用企业微信审核详情接口, 并处理返回数据
|
||||||
|
* @param spNo 审批单号
|
||||||
|
*/
|
||||||
|
public async parseOneTask(spNo: string) {
|
||||||
|
let detail: any = await this.fetchApprovalDetail(spNo)
|
||||||
|
if (detail.errcode) {
|
||||||
|
throw new Error('approval detail error, code: ' + detail.errcode + ' errmsg: ' + detail.errmsg)
|
||||||
|
}
|
||||||
|
const { info } = detail
|
||||||
|
if (info.status !== TaskStatus.PASS) {
|
||||||
|
throw new Error('approval status error, status: ' + info.status)
|
||||||
|
}
|
||||||
|
const { apply_data } = info
|
||||||
|
const { contents } = apply_data
|
||||||
|
let name = ''
|
||||||
|
let desc = ''
|
||||||
|
let fileId = ''
|
||||||
|
for (let content of contents) {
|
||||||
|
let { control, value, title } = content
|
||||||
|
if (control === 'Text' && title.text == '名字') {
|
||||||
|
name = value.text
|
||||||
|
} else if (control === 'Text' && title.text == '描述') {
|
||||||
|
desc = value.text
|
||||||
|
} else if (control === 'File' && value.files.length > 0) {
|
||||||
|
fileId = value.files[0].file_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!fileId) {
|
||||||
|
throw new Error('no file')
|
||||||
|
}
|
||||||
|
let filePath = await this.fetchFile(fileId)
|
||||||
|
let data = excelToJson(filePath)
|
||||||
|
return { taskId: spNo, name, desc, data }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -91,8 +141,11 @@ export class WechatWorkService {
|
|||||||
media_id: mediaId,
|
media_id: mediaId,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
let response = await axios.request(config).then(response => {
|
let filename = `${mediaId}.xlsx`
|
||||||
response.data.pipe(fs.createWriteStream('ada_lovelace.jpg'))
|
const filePath = path.join(os.tmpdir(), filename)
|
||||||
|
await axios.request(config).then(response => {
|
||||||
|
response.data.pipe(fs.createWriteStream(filePath))
|
||||||
})
|
})
|
||||||
|
return filePath
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user