"use strict";
/**
 * 装饰器模块
 * @date 2022-03
 * @author 何华东
 */
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __generator = (this && this.__generator) || function (thisArg, body) {
    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
    return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
    function verb(n) { return function (v) { return step([n, v]); }; }
    function step(op) {
        if (f) throw new TypeError("Generator is already executing.");
        while (_) try {
            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
            if (y = 0, t) op = [op[0] & 2, t.value];
            switch (op[0]) {
                case 0: case 1: t = op; break;
                case 4: _.label++; return { value: op[1], done: false };
                case 5: _.label++; y = op[1]; op = [0]; continue;
                case 7: op = _.ops.pop(); _.trys.pop(); continue;
                default:
                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                    if (t[2]) _.ops.pop();
                    _.trys.pop(); continue;
            }
            op = body.call(thisArg, _);
        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
    }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Throttle = exports.Sync = exports.Retryable = exports.retry = exports.CronJob = void 0;
var typescript_ioc_1 = require("typescript-ioc");
var mslib_1 = require("mslib");
var schedule = require("node-schedule");
var Contants_1 = require("./Contants");
var Errors_1 = require("./Errors");
var dayjs = require("dayjs");
/**
 * Cron计划任务
 *
 * @param cronExpression cron时间表达式
 * @param description 计划任务描述,仅为记录日志
 * @returns
 */
function CronJob(cronExpression, description) {
    return function (target) {
        var _this = this;
        mslib_1.logger.info(Contants_1.default.SS_SERVICE_BASE_URL + "/" + Contants_1.default.P_ID + "." + target.name + "/1.0 " + description + "(" + cronExpression + ")");
        //支持开关,一般用于测试环境下禁用计划任务
        if (!typescript_ioc_1.Container.getValue('config.cron.enabled'))
            return;
        schedule.scheduleJob(cronExpression, function () { return __awaiter(_this, void 0, void 0, function () {
            var service, error_1;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        _a.trys.push([0, 2, , 3]);
                        service = typescript_ioc_1.Container.get(target);
                        return [4 /*yield*/, service.doService()];
                    case 1:
                        _a.sent();
                        return [3 /*break*/, 3];
                    case 2:
                        error_1 = _a.sent();
                        mslib_1.logger.error("" + (description || '定时任务') + target.name + "\u51FA\u73B0\u5F02\u5E38", "\n\u5F02\u5E38\u4FE1\u606F>>>>>", error_1 instanceof Errors_1.ApiError && !Contants_1.default.IS_DEBUG ? error_1.toString() : error_1);
                        return [3 /*break*/, 3];
                    case 3: return [2 /*return*/];
                }
            });
        }); });
    };
}
exports.CronJob = CronJob;
/**
 * 重试器
 *
 * 重试条件:
 *
 * 1.如果可重试目标方法出现异常,会自动进行重试
 *
 * 2.可重试目标方法未抛出异常,根据返回结果并传入重试条件retryCondition方法判断是否进行重试
 * @param func 可重试的目标方法
 * @param retryMaxCount 重试最大次数
 * @param retryDelayMs 每次重试延迟毫秒数
 * @param errorHandler 可重试的目标方法出现异常时的异常处理,不会打断重试逻辑，一般用于记录日志
 * @param retryCondition 重试条件,传入可重试的目标方法返回的结果判断是否重试
 */
function retry(func, retryMaxCount, retryDelayMs, config) {
    var _this = this;
    return new Promise(function (resolve, reject) { return __awaiter(_this, void 0, void 0, function () {
        var res, error_2;
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0:
                    _a.trys.push([0, 2, , 3]);
                    return [4 /*yield*/, func()];
                case 1:
                    res = _a.sent();
                    if (typeof (config === null || config === void 0 ? void 0 : config.retryCondition) !== 'function' || !config.retryCondition(res)) {
                        resolve(res);
                        return [2 /*return*/];
                    }
                    return [3 /*break*/, 3];
                case 2:
                    error_2 = _a.sent();
                    if (typeof (config === null || config === void 0 ? void 0 : config.errorHandler) === 'function')
                        config.errorHandler(error_2, retryMaxCount);
                    return [3 /*break*/, 3];
                case 3:
                    if (retryMaxCount <= 0) {
                        reject('已用尽所有尝试次数');
                    }
                    else {
                        setTimeout(function () {
                            resolve(retry(func, --retryMaxCount, retryDelayMs, config));
                        }, retryDelayMs);
                    }
                    return [2 /*return*/];
            }
        });
    }); });
}
exports.retry = retry;
/**
 * 重试器
 *
 * 重试条件:
 *
 * 1.如果可重试目标方法出现异常,会自动进行重试
 *
 * 2.可重试目标方法未抛出异常,根据返回结果并传入重试条件retryCondition方法判断是否进行重试
 * @param methodDesc 方法描述,仅为记录日志
 * @param retryMaxCount 重试最大次数
 * @param retryDelayMs 每次重试延迟毫秒数
 * @param errorHandler 可重试的目标方法出现异常时的异常处理,不会打断重试逻辑，一般用于记录日志
 * @param retryCondition 重试条件,传入可重试的目标方法返回的结果判断是否重试
 */
function Retryable(methodDesc, retryMaxCount, retryDelayMs, config) {
    return function (target, key, descriptor) {
        var method = descriptor.value;
        descriptor.value = function () {
            var args = [];
            for (var _i = 0; _i < arguments.length; _i++) {
                args[_i] = arguments[_i];
            }
            return __awaiter(this, void 0, void 0, function () {
                var result, error_3;
                var _this = this;
                return __generator(this, function (_a) {
                    switch (_a.label) {
                        case 0:
                            _a.trys.push([0, 2, , 3]);
                            return [4 /*yield*/, retry(function () { return __awaiter(_this, void 0, void 0, function () {
                                    return __generator(this, function (_a) {
                                        switch (_a.label) {
                                            case 0: return [4 /*yield*/, method.apply(this, args)];
                                            case 1: return [2 /*return*/, _a.sent()];
                                        }
                                    });
                                }); }, retryMaxCount || 1, retryDelayMs || 1000 * 60, {
                                    errorHandler: function (error, retryCountLeft) {
                                        mslib_1.logger.error("" + (methodDesc || '') + target.constructor.name + "/" + key + "\u51FA\u73B0\u5F02\u5E38(\u5DF2\u91CD\u8BD5" + (retryMaxCount - retryCountLeft) + "\u6B21}),\u65B9\u6CD5\u53C2\u6570:" + JSON.stringify(args), "\n\u5F02\u5E38\u4FE1\u606F>>>>>", error instanceof Errors_1.ApiError && !Contants_1.default.IS_DEBUG ? error.toString() : error);
                                    },
                                    retryCondition: config === null || config === void 0 ? void 0 : config.retryCondition
                                })];
                        case 1:
                            result = _a.sent();
                            return [2 /*return*/, result];
                        case 2:
                            error_3 = _a.sent();
                            mslib_1.logger.error("" + (methodDesc || '') + target.constructor.name + "/" + key + "\u51FA\u73B0\u5F02\u5E38,\u65B9\u6CD5\u53C2\u6570:" + JSON.stringify(args), "\n\u5F02\u5E38\u4FE1\u606F>>>>>", error_3);
                            return [2 /*return*/, null];
                        case 3: return [2 /*return*/];
                    }
                });
            });
        };
    };
}
exports.Retryable = Retryable;
/**
 * 异步锁
 */
var AsyncLock = /** @class */ (function () {
    function AsyncLock() {
        this.promise = Promise.resolve();
    }
    AsyncLock.prototype.enable = function () {
        var _this = this;
        this.promise = new Promise(function (resolve) { return _this.disable = resolve; });
    };
    return AsyncLock;
}());
/**
 * 异步方法同步执行器
 *
 * 基于Promise实现
 */
function Sync() {
    return function (target, key, descriptor) {
        var method = descriptor.value;
        var lock = new AsyncLock();
        descriptor.value = function () {
            var args = [];
            for (var _i = 0; _i < arguments.length; _i++) {
                args[_i] = arguments[_i];
            }
            return __awaiter(this, void 0, void 0, function () {
                return __generator(this, function (_a) {
                    switch (_a.label) {
                        case 0: return [4 /*yield*/, lock.promise];
                        case 1:
                            _a.sent();
                            lock.enable();
                            try {
                                return [2 /*return*/, method.apply(this, args)];
                            }
                            catch (error) {
                                throw error;
                            }
                            finally {
                                lock.disable();
                            }
                            return [2 /*return*/];
                    }
                });
            });
        };
    };
}
exports.Sync = Sync;
/**
 * 节流器
 * @param milliseconds 统计周期
 * @param count 最大执行次数
 * @param filter 过滤器,返回true表示不参与节流
 */
function Throttle(milliseconds, count, filter) {
    return function (target, key, descriptor) {
        var method = descriptor.value;
        var count_time = dayjs();
        var counter = 0;
        descriptor.value = function () {
            var args = [];
            for (var _i = 0; _i < arguments.length; _i++) {
                args[_i] = arguments[_i];
            }
            return __awaiter(this, void 0, void 0, function () {
                var now;
                return __generator(this, function (_a) {
                    switch (_a.label) {
                        case 0:
                            now = dayjs();
                            if (!(dayjs(now).diff(count_time, 'millisecond') > milliseconds)) return [3 /*break*/, 1];
                            count_time = now;
                            counter = 1;
                            return [3 /*break*/, 4];
                        case 1:
                            counter++;
                            if (!(counter > count)) return [3 /*break*/, 4];
                            if (!(typeof filter === 'function' && filter(args) === true)) return [3 /*break*/, 3];
                            return [4 /*yield*/, method.apply(this, args)];
                        case 2: return [2 /*return*/, _a.sent()];
                        case 3:
                            mslib_1.logger.warn(target.constructor.name + "\u5DF2\u88AB\u622A\u65AD" + counter + ">" + count + "(" + milliseconds + "ms)},args:" + JSON.stringify(args));
                            return [2 /*return*/];
                        case 4: return [4 /*yield*/, method.apply(this, args)];
                        case 5: return [2 /*return*/, _a.sent()];
                    }
                });
            });
        };
    };
}
exports.Throttle = Throttle;
//# sourceMappingURL=Decorators.js.map