wistroni40-bnft
Version:
Benefit platform parameters publish job template
346 lines (328 loc) • 9.62 kB
text/typescript
/**
* 專案名稱: @wistroni40/bnft
* 部門代號: ML8100
* 檔案說明: 抽象效益計算範本
* @CREATE Thu Jan 21 2021 下午1:44:54
* @author Steve Y Lin
* @contact Steve_Y_Lin@wistron.com #1342
* -----------------------------------------------------------------------------
* @NOTE
*/
import axios from 'axios';
import * as schedule from 'node-schedule';
import { from, Observable, of, Subject } from 'rxjs';
import { catchError, concatAll, filter, map } from 'rxjs/operators';
import { Server } from './../api';
import { AxiosHttpService, HttpAdapter, HttpResponse } from './../http';
import { Log4js } from './../logger';
import {
HttpProducer,
ProducePayloadEntity as ProducePayload,
ProducePayloadModel,
} from './../retry';
import { TimeManager } from './../utils';
import {
ApiConfig,
BaseModel as ApiBaseModel,
BenefitActivedSystemModel as ActivedSystemModel,
BenefitActivedSystemService,
BenefitLaborCostService,
BenefitLatestLaborCostResponse,
BenefitPlantModel as PlantModel,
} from './api';
import { BenefitLatestLaborCostEntity as LaborCost } from './api/models';
import { BenefitQueryConvertor } from './classes';
import {
BenefitConfigEntity,
BenefitConfigModel,
BenefitQueryModel,
BenefitSavingEntity,
Bnft,
} from './models';
/**
* 效益上拋回傳結果
*/
export type BenefitResponse = {
error: any;
result: ProducePayloadModel<Bnft.BenefitSaving>;
};
/**
* 抽象效益計算範本
*/
export abstract class BnftTemplate<C = any> {
/**
* 日誌
*/
private readonly console = new Log4js('bnft');
/**
* API服務器
*/
private apiServer = Server.instance.register(this);
/**
* HTTP請求
*/
protected http: HttpAdapter = new AxiosHttpService(axios);
/**
* HTTP拋送者
*/
protected producer: HttpProducer;
/**
* 效益激活系統服務
*/
protected activedSystemService = new BenefitActivedSystemService(this.http);
/**
* 效益DL及IDL人員工時服務
*/
protected benefitLaborCostService = new BenefitLaborCostService(this.http);
/**
* 排程設定
*/
protected cron: string | null = null;
/**
* 需要計算的廠別
*/
protected abstract enabledPlant?: string[];
/**
* 送出數據完畢
*/
public sendCompleted: Subject<BenefitResponse>;
/**
* @param config 效益設定檔
*/
constructor(public config: BenefitConfigModel<C>) {
this.config = new BenefitConfigEntity(this.config);
ApiConfig.path = this.config.benefitApi;
this.producer = new HttpProducer(this.http, {
count: this.config.retry,
interval: this.config.retryInterval,
});
this.sendCompleted = this.producer.sendCompleted;
}
/**
* 取得預設的效益參數資料
*
* @method private
* @return 回傳預設的效益參數資料
*/
private getDefaultProcucePayload(): Observable<
ProducePayloadModel<Bnft.BenefitSaving>
> {
return of(
new ProducePayload(this.config.publishApi, new BenefitSavingEntity()),
);
}
/**
* 建構效益參數實體
*
* @method protected
* @param timestamp 資料時間戳
* @param condition 效益查詢條件
* @param params 效益參數
*/
protected buildBenefitEntity(
timestamp: Date,
condition: BenefitQueryModel,
params: Bnft.Param[],
): Bnft.BenefitSaving {
return new BenefitSavingEntity({
evt_dt: timestamp.getTime(),
freq: 'D',
site: condition.site,
company: condition.company,
plant: condition.plant,
plant_code: condition.plantCode,
system_id: this.config.systemId,
type_id: this.config.typeId,
benefit_type: this.config.benefitType,
params,
});
}
/**
* 建構查詢激活系統的查詢條件
*
* @method protected
* @return 回傳查詢條件
*/
protected buildQueryActivatedSystemsFilter(): ApiBaseModel.Filter {
return {
include: 'benefitPlant',
where: { system_id: this.config.systemId },
};
}
/**
* 查詢效益參數
*
* @method protected
* @param plant 廠別資料作為查詢條件
* @param timestamp 查詢開始時間
* @return 回傳效益參數
*/
protected queryBenefit(
plant: PlantModel,
timestamp?: Date,
): Observable<Bnft.BenefitSaving> {
const start = TimeManager.getStartTime(timestamp);
const end = TimeManager.getEndTime(timestamp);
const condition = new BenefitQueryConvertor(plant)
.setStartTime(start)
.setEndTime(end)
.build();
return this.buildPayload(start, condition);
}
/**
* 設定查詢用的HTTP
*
* @method public
* @param http HTTP請求
* @return 回傳物件本身
*/
public setHttp(http: HttpAdapter): BnftTemplate {
this.http = http;
this.activedSystemService = new BenefitActivedSystemService(this.http);
return this;
}
/**
* 設定排程計算效益
*
* @method public
* @param cron 排程
* @return 回傳物件本身
*/
public setSchedule(cron: string): Observable<BenefitResponse> {
this.cron = cron;
if (this.cron) {
schedule.scheduleJob(this.cron, this.execute.bind(this));
}
return this.sendCompleted;
}
/**
* 過濾無須或異常的廠別
*
* @method public
* @param plant 廠別資料
* @return 回傳該廠別是否要保留
*/
public filterPlant(plant?: PlantModel): boolean {
const isNotUndefined = plant !== undefined;
let isAllowedPlant = true;
if (plant && this.enabledPlant && this.enabledPlant.length > 0) {
isAllowedPlant = this.enabledPlant.includes(plant.plantcode);
}
return isNotUndefined && isAllowedPlant;
}
/**
* 建構效益參數資料
*
* @method public
* @param timestamp 資料時間戳
* @param condition 效益查詢條件
* @return 回傳效益參數資料
*/
public buildPayload(
timestamp: Date,
condition: BenefitQueryModel,
): Observable<Bnft.BenefitSaving> {
return from(this.getBenefitParams(condition)).pipe(
map(params => this.buildBenefitEntity(timestamp, condition, params)),
);
}
/**
* 取得效益參數
*
* @method public
* @param condition 效益查詢條件
* @return 回傳效益參數
*/
public abstract getBenefitParams(
condition: BenefitQueryModel,
): Promise<Bnft.Param[]>;
/**
* 處理效益參數
*
* @method public
* @param response 查詢激活的效益系統
* @param timestamp 查詢開始時間
* @return 回傳處理後效益參數
*/
public processBenefitParams(
response: Observable<HttpResponse<ActivedSystemModel[]>>,
timestamp?: Date,
): Observable<ProducePayloadModel<Bnft.BenefitSaving>> {
return response.pipe(
// 取出HTTP回應的資料
map(res => res.data),
// 從系統資料中取出廠別資料
map(systems => systems.map(system => system.benefitPlant)),
// 將廠別資料重新建成Subject
map(plants => from(plants)),
// 將廠別打散成單筆數據
concatAll(),
// 保留需要計算及無異常的廠別
filter(plant => this.filterPlant(plant)),
// 查詢效益參數
map(plant => this.queryBenefit(plant as PlantModel, timestamp)),
// 將效益參數打散成單筆數據
concatAll(),
// 打包成拋送的資料格式
map(benefit => new ProducePayload(this.config.publishApi, benefit)),
// 發生錯誤給定預設值
catchError(() => this.getDefaultProcucePayload()),
);
}
/**
* 執行效益參數撈取
*
* @method public
* @param timestamp 查詢開始時間
* @return 回傳效益參數上拋結果
*/
public execute(timestamp?: Date): Observable<BenefitResponse> {
const dev = this.config.dev;
const query = this.buildQueryActivatedSystemsFilter();
const system$ = this.activedSystemService.find<ActivedSystemModel>(query);
const param$ = this.processBenefitParams(system$, timestamp);
param$.subscribe(payload => this.send(payload, !dev));
return this.sendCompleted;
}
/**
* 將效益參數上拋
*
* @method public
* @param payload 效益參數
* @param sendable 效益參數是否上拋
*/
public async send(
payload: ProducePayloadModel<Bnft.BenefitSaving>,
sendable = true,
): Promise<void> {
if (sendable) {
this.producer.publish(payload);
} else {
this.console.debug(JSON.stringify(payload));
this.sendCompleted.next({ error: null, result: payload });
}
}
/**
* 取得最新的IDL或ID人員工時
*
* @method public
* @param site Site
* @param plantCode 廠別代碼
* @param laborType 人員類別(IDL or DL)
* @return 回傳最新的IDL或ID人員工時
*/
public findLatestLaborCost(
site: string,
plantCode: string,
type: 'idl' | 'dl',
): Observable<BenefitLatestLaborCostResponse> {
return this.benefitLaborCostService
.getLatestCost(site, plantCode, type)
.pipe(
// 將工時資料取出
map(response => response.data),
// 發生錯誤給定預設值
catchError(() => of(new LaborCost({ site, plant: plantCode, type }))),
);
}
}