一些代码移至公用组件
This commit is contained in:
parent
cf5d4c659c
commit
a153eded7b
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "packages/zutils"]
|
||||
path = packages/zutils
|
||||
url = git@git.kingsome.cn:zhanghongliang/zutils.git
|
@ -37,7 +37,8 @@
|
||||
"node-schedule": "^2.1.1",
|
||||
"redis": "^3.1.2",
|
||||
"tracer": "^1.1.6",
|
||||
"verify-apple-id-token": "^3.0.0"
|
||||
"verify-apple-id-token": "^3.0.0",
|
||||
"zutils": "link:packages/zutils"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@typegoose/typegoose": "^9.12.1",
|
||||
|
1
packages/zutils
Submodule
1
packages/zutils
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit b982e43cd717e09acafba79f40c63e2572dbef96
|
@ -1,7 +1,6 @@
|
||||
import fastify, { FastifyError, FastifyInstance, FastifyReply, FastifyRequest } from 'fastify'
|
||||
import helmet from '@fastify/helmet'
|
||||
import { IncomingMessage, Server, ServerResponse } from 'http'
|
||||
import { RouterMap } from 'decorators/router'
|
||||
import { mongoose } from '@typegoose/typegoose'
|
||||
import logger from 'logger/logger'
|
||||
import config from 'config/config'
|
||||
@ -10,6 +9,7 @@ import CodeTaskSchedule from 'schedule/codetask.schedule'
|
||||
import { PriceSvr } from 'service/price.svr'
|
||||
import { GooglePaySvr } from 'service/googlepay.svr'
|
||||
import { GasSvr } from 'service/gas.svr'
|
||||
import { RouterMap } from 'zutils'
|
||||
|
||||
const zReqParserPlugin = require('plugins/zReqParser')
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { singleton } from '../decorators/singleton'
|
||||
import { singleton } from 'zutils'
|
||||
import Clock from './ClockTimer'
|
||||
import { Delayed } from './Delayed'
|
||||
|
||||
|
@ -1,107 +0,0 @@
|
||||
type Callback<T> = () => Promise<T>
|
||||
|
||||
export type AsyncQueue<T = void> = {
|
||||
push: (task: Callback<T>) => Promise<T>
|
||||
flush: () => Promise<void>
|
||||
size: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that each callback pushed onto the queue is executed in series.
|
||||
* Such a quetie 😻
|
||||
* @param opts.dedupeConcurrent If dedupeConcurrent is `true` it ensures that if multiple
|
||||
* tasks are pushed onto the queue while there is an active task, only the
|
||||
* last one will be executed, once the active task has completed.
|
||||
* e.g. in the below example, only 0 and 3 will be executed.
|
||||
* ```
|
||||
* const queue = createAsyncQueue({ dedupeConcurrent: true })
|
||||
* queue.push(async () => console.log(0)) // returns 0
|
||||
* queue.push(async () => console.log(1)) // returns 3
|
||||
* queue.push(async () => console.log(2)) // returns 3
|
||||
* queue.push(async () => console.log(3)) // returns 3
|
||||
* ```
|
||||
* */
|
||||
export function createAsyncQueue<T = void>(opts = { dedupeConcurrent: false }): AsyncQueue<T> {
|
||||
const { dedupeConcurrent } = opts
|
||||
let queue: Callback<T>[] = []
|
||||
let running: Promise<void> | undefined
|
||||
let nextPromise = new DeferredPromise<T>()
|
||||
const push = (task: Callback<T>) => {
|
||||
let taskPromise = new DeferredPromise<T>()
|
||||
if (dedupeConcurrent) {
|
||||
queue = []
|
||||
if (nextPromise.started) nextPromise = new DeferredPromise<T>()
|
||||
taskPromise = nextPromise
|
||||
}
|
||||
queue.push(() => {
|
||||
taskPromise.started = true
|
||||
task().then(taskPromise.resolve).catch(taskPromise.reject)
|
||||
return taskPromise.promise
|
||||
})
|
||||
if (!running) running = start()
|
||||
return taskPromise.promise
|
||||
}
|
||||
const start = async () => {
|
||||
while (queue.length) {
|
||||
const task = queue.shift()!
|
||||
await task().catch(() => {})
|
||||
}
|
||||
running = undefined
|
||||
}
|
||||
return {
|
||||
push,
|
||||
flush: () => running || Promise.resolve(),
|
||||
get size() {
|
||||
return queue.length
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export const createAsyncQueues = <T = void>(opts = { dedupeConcurrent: false }) => {
|
||||
const queues: { [queueId: string]: AsyncQueue<T> } = {}
|
||||
const push = (queueId: string, task: Callback<T>) => {
|
||||
if (!queues[queueId]) queues[queueId] = createAsyncQueue<T>(opts)
|
||||
return queues[queueId].push(task)
|
||||
}
|
||||
const flush = (queueId: string) => {
|
||||
if (!queues[queueId]) queues[queueId] = createAsyncQueue<T>(opts)
|
||||
return queues[queueId].flush()
|
||||
}
|
||||
return { push, flush }
|
||||
}
|
||||
|
||||
class DeferredPromise<T = void, E = any> {
|
||||
started = false
|
||||
resolve: (x: T | PromiseLike<T>) => void = () => {}
|
||||
reject: (x: E) => void = () => {}
|
||||
promise: Promise<T>
|
||||
|
||||
constructor() {
|
||||
this.promise = new Promise<T>((res, rej) => {
|
||||
this.resolve = res
|
||||
this.reject = rej
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// function main() {
|
||||
// const queue = createAsyncQueue()
|
||||
// queue.push(async () => {
|
||||
// console.log(0)
|
||||
// }) // returns 0
|
||||
// queue.push(async () => {
|
||||
// console.log(1)
|
||||
|
||||
// return new Promise((resolve, reject) => {
|
||||
// setTimeout(() => {
|
||||
// console.log('12')
|
||||
// resolve()
|
||||
// }, 1000)
|
||||
// })
|
||||
// }) // returns 3
|
||||
// queue.push(async () => console.log(2)) // returns 3
|
||||
// queue.push(async () => console.log(3)) // returns 3
|
||||
// console.log('hi')
|
||||
// }
|
||||
|
||||
// main()
|
@ -1,36 +0,0 @@
|
||||
import { singleton } from "decorators/singleton";
|
||||
import { FastifyRequest } from "fastify";
|
||||
import { ZError } from "./ZError";
|
||||
|
||||
@singleton
|
||||
export class SyncLocker {
|
||||
map: Map<string, boolean> = new Map();
|
||||
|
||||
public lock(req: FastifyRequest) {
|
||||
const key = `${req.method}:${req.url}:${req.user?.id || ''}`
|
||||
if (this.map.has(key)) {
|
||||
return false;
|
||||
}
|
||||
this.map.set(key, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
public unlock(req: FastifyRequest) {
|
||||
const key = `${req.method}:${req.url}:${req.user?.id || ''}`
|
||||
this.map.delete(key);
|
||||
}
|
||||
|
||||
public checkLock(req: FastifyRequest) {
|
||||
const key = `${req.method}:${req.url}:${req.user?.id || ''}`
|
||||
if (this.map.has(key)) {
|
||||
throw new ZError(100, 'request too fast');
|
||||
}
|
||||
this.lock(req);
|
||||
return true;
|
||||
}
|
||||
|
||||
public isLocked(req: FastifyRequest) {
|
||||
const key = `${req.method}:${req.url}:${req.user?.id || ''}`
|
||||
return this.map.has(key);
|
||||
}
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
import { FastifyError } from 'fastify'
|
||||
|
||||
export class ZError implements FastifyError {
|
||||
code: string
|
||||
statusCode?: number
|
||||
message: string
|
||||
name: string
|
||||
|
||||
constructor(statusCode: number, message: string) {
|
||||
this.statusCode = statusCode
|
||||
this.message = message
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
import fastify = require('fastify')
|
||||
|
||||
export const ROLE_ANON = 'anon'
|
||||
class BaseController {
|
||||
aotoRoute(req: fastify.FastifyRequest, res) {}
|
||||
}
|
||||
export default BaseController
|
@ -1,12 +1,10 @@
|
||||
import logger from 'logger/logger'
|
||||
import BaseController, { ROLE_ANON } from 'common/base.controller'
|
||||
import { ZError } from 'common/ZError'
|
||||
import { role, router } from 'decorators/router'
|
||||
import { checkPrePaySign, createOrder, queryFiat, queryPrice, refreshToken } from 'service/alchemy.svr'
|
||||
import { PayRecord, PayStatus } from 'modules/PayRecord'
|
||||
import { PriceSvr } from 'service/price.svr'
|
||||
import { reportPayResult } from 'service/game.svr'
|
||||
import { GasSvr } from 'service/gas.svr'
|
||||
import { BaseController, ROLE_ANON, ZError, role, router } from 'zutils'
|
||||
|
||||
const CALL_BACK_URL = `${process.env.ALCHEMY_PAY_CB_URL}/pay/out/alchemy/result`
|
||||
class AlchemyController extends BaseController {
|
||||
|
@ -1,7 +1,4 @@
|
||||
import logger from 'logger/logger'
|
||||
import BaseController, { ROLE_ANON } from 'common/base.controller'
|
||||
import { ZError } from 'common/ZError'
|
||||
import { role, router } from 'decorators/router'
|
||||
import { calcNetworkFee, checkPayResultSign, checkSha1Sign } from 'service/alchemy.svr'
|
||||
import { PayRecord, PayStatus } from 'modules/PayRecord'
|
||||
import { TransferQueue } from 'queue/transfer.queue'
|
||||
@ -9,6 +6,7 @@ import { TransferRecord } from 'modules/TransferRecord'
|
||||
import { reportPayResult } from 'service/game.svr'
|
||||
import { PriceSvr } from 'service/price.svr'
|
||||
import { OrderCacheSvr } from 'service/ordercache.svr'
|
||||
import { BaseController, role, ROLE_ANON, router, ZError } from 'zutils'
|
||||
import { fromWei, toBigWei } from 'utils/number.util'
|
||||
|
||||
let errorRes = function (msg: string) {
|
||||
|
@ -1,11 +1,9 @@
|
||||
import { ZError } from 'common/ZError'
|
||||
import BaseController, { ROLE_ANON } from 'common/base.controller'
|
||||
import { role, router } from 'decorators/router'
|
||||
import logger from 'logger/logger'
|
||||
import { AppleInApp, ApplePayStatus } from 'modules/AppleInApp'
|
||||
import { InAppRecord } from 'modules/InAppRecord'
|
||||
import { IPayResult, reportApplePurchaseResult } from 'service/game.svr'
|
||||
import { IosPaySvr } from 'service/iospay.svr'
|
||||
import { BaseController, router, ZError, role, ROLE_ANON } from 'zutils'
|
||||
|
||||
class ApplePayController extends BaseController {
|
||||
@router('post /pay/apple/verify')
|
||||
|
@ -1,11 +1,9 @@
|
||||
import BaseController from 'common/base.controller'
|
||||
import { ZError } from 'common/ZError'
|
||||
import { router } from 'decorators/router'
|
||||
import logger from 'logger/logger'
|
||||
import { GoogleInApp, GooglePayStatus } from 'modules/GoogleInApp'
|
||||
import { InAppRecord } from 'modules/InAppRecord'
|
||||
import { IPayResult, reportGooglePurchaseResult } from 'service/game.svr'
|
||||
import { GooglePaySvr } from 'service/googlepay.svr'
|
||||
import { BaseController, router, ZError } from 'zutils'
|
||||
|
||||
class GooglePayController extends BaseController {
|
||||
@router('post /pay/google/verify')
|
||||
|
@ -1,18 +1,16 @@
|
||||
import { ZError } from 'common/ZError'
|
||||
import BaseController, { ROLE_ANON } from 'common/base.controller'
|
||||
import { role, router } from 'decorators/router'
|
||||
import logger from 'logger/logger'
|
||||
import { PayRecord, PayRecordClass, PayStatus } from 'modules/PayRecord'
|
||||
import { TransferRecord, TransferRecordClass } from 'modules/TransferRecord'
|
||||
import { hmacsha256 } from 'utils/security.util'
|
||||
import { DocumentType } from '@typegoose/typegoose'
|
||||
import { updateOrderStatus } from 'service/alchemy.svr'
|
||||
import { PriceSvr } from 'service/price.svr'
|
||||
import { assembleGameData, checkGameSign } from 'service/game.svr'
|
||||
import { BaseController, role, ROLE_ANON, router, ZError } from 'zutils'
|
||||
import { hmacSha256 } from 'zutils/utils/security.util'
|
||||
|
||||
const calcHash = function (data: any) {
|
||||
let signStr = JSON.stringify(data)
|
||||
return hmacsha256(signStr, process.env.HASH_SALT_CHAIN)
|
||||
return hmacSha256(signStr, process.env.HASH_SALT_CHAIN).toLowerCase()
|
||||
}
|
||||
|
||||
const notify = async function (record: DocumentType<PayRecordClass>, subTask: DocumentType<TransferRecordClass>) {
|
||||
|
@ -1,7 +1,5 @@
|
||||
import { ZError } from 'common/ZError'
|
||||
import BaseController, { ROLE_ANON } from 'common/base.controller'
|
||||
import { role, router } from 'decorators/router'
|
||||
import { beginSell, buyOrder, submitOrder } from 'service/okxmarket.svr'
|
||||
import { BaseController, ROLE_ANON, ZError, role, router } from 'zutils'
|
||||
|
||||
class OkxMarketController extends BaseController {
|
||||
@role(ROLE_ANON)
|
||||
|
@ -1,7 +1,5 @@
|
||||
import BaseController, { ROLE_ANON } from 'common/base.controller'
|
||||
import { ZError } from 'common/ZError'
|
||||
import { role, router } from 'decorators/router'
|
||||
import logger from 'logger/logger'
|
||||
import { BaseController, role, ROLE_ANON, router } from 'zutils'
|
||||
|
||||
class PurchaseController extends BaseController {
|
||||
/**
|
||||
|
@ -1,7 +1,5 @@
|
||||
import BaseController, { ROLE_ANON } from 'common/base.controller'
|
||||
import { ZError } from 'common/ZError'
|
||||
import { role, router } from 'decorators/router'
|
||||
import { BridgeSvr } from 'service/bridge.svr'
|
||||
import { BaseController, role, ROLE_ANON, router, ZError } from 'zutils'
|
||||
|
||||
const TOKEN_PREFIX = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.'
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import 'reflect-metadata'
|
||||
import { singleton } from './singleton'
|
||||
import { singleton } from 'zutils'
|
||||
|
||||
@singleton
|
||||
export class NoJsonClass {
|
||||
|
@ -1,142 +0,0 @@
|
||||
import BaseController from '../common/base.controller'
|
||||
|
||||
export class RouterData {
|
||||
target?: any
|
||||
method?: string
|
||||
path?: string
|
||||
fun?: Function
|
||||
}
|
||||
|
||||
export class RouterMap {
|
||||
static decoratedRouters: Map<
|
||||
Function,
|
||||
{
|
||||
roles?: string[]
|
||||
permissions?: string[][]
|
||||
data?: RouterData[]
|
||||
depts?: string[]
|
||||
}
|
||||
> = new Map()
|
||||
}
|
||||
|
||||
export function router(route?: string) {
|
||||
return (target: BaseController, name: string, value: PropertyDescriptor) => {
|
||||
if (!route) {
|
||||
const controller = target.constructor.name
|
||||
const controllerName = controller.toLowerCase().replace('.controller', '')
|
||||
route = 'all ' + ['', controllerName, name].join('/')
|
||||
}
|
||||
const split = route.split(' ')
|
||||
if (split.length > 2) {
|
||||
throw new Error('路由中只允许一个空格')
|
||||
}
|
||||
const [method, path] = split
|
||||
// @ts-ignore
|
||||
const key = target[name]
|
||||
let routerData = new RouterData()
|
||||
routerData.target = target
|
||||
routerData.method = method
|
||||
routerData.path = path
|
||||
// @ts-ignore
|
||||
routerData.fun = target[name]
|
||||
|
||||
if (RouterMap.decoratedRouters.has(key)) {
|
||||
let objCurrent = RouterMap.decoratedRouters.get(key)
|
||||
if (!objCurrent.data) {
|
||||
objCurrent.data = [routerData]
|
||||
} else {
|
||||
objCurrent.data.push(routerData)
|
||||
}
|
||||
// @ts-ignore
|
||||
RouterMap.decoratedRouters.set(target[name], objCurrent)
|
||||
} else {
|
||||
let routerObj = {
|
||||
data: [routerData],
|
||||
}
|
||||
// @ts-ignore
|
||||
RouterMap.decoratedRouters.set(target[name], routerObj)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function role(roles?: string | string[]) {
|
||||
return (target: BaseController, name: string, value: PropertyDescriptor) => {
|
||||
let roleList: string[] = []
|
||||
if (roles) {
|
||||
if (Array.isArray(roles)) {
|
||||
roleList = roles
|
||||
} else {
|
||||
roleList = [roles]
|
||||
}
|
||||
}
|
||||
// @ts-ignore
|
||||
const key = target[name]
|
||||
let roleObj = { roles: roleList }
|
||||
if (RouterMap.decoratedRouters.has(key)) {
|
||||
let objCurrent = RouterMap.decoratedRouters.get(key)
|
||||
Object.assign(objCurrent, roleObj)
|
||||
// @ts-ignore
|
||||
RouterMap.decoratedRouters.set(target[name], objCurrent)
|
||||
} else {
|
||||
// @ts-ignore
|
||||
RouterMap.decoratedRouters.set(target[name], roleObj)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function permission(permissions?: string | string[]) {
|
||||
return (target: BaseController, name: string, value: PropertyDescriptor) => {
|
||||
let permissionList: string[][] = [[]]
|
||||
if (permissions) {
|
||||
if (Array.isArray(permissions)) {
|
||||
let arr = []
|
||||
for (let sub of permissions) {
|
||||
arr.push(sub.split(':'))
|
||||
}
|
||||
permissionList = arr
|
||||
} else {
|
||||
permissionList = [permissions.split(':')]
|
||||
}
|
||||
}
|
||||
// @ts-ignore
|
||||
const key = target[name]
|
||||
let permissionObj = { permissions: permissionList }
|
||||
if (RouterMap.decoratedRouters.has(key)) {
|
||||
let objCurrent = RouterMap.decoratedRouters.get(key)
|
||||
Object.assign(objCurrent, permissionObj)
|
||||
// @ts-ignore
|
||||
RouterMap.decoratedRouters.set(target[name], objCurrent)
|
||||
} else {
|
||||
// @ts-ignore
|
||||
RouterMap.decoratedRouters.set(target[name], permissionObj)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 有dept修饰器的, 需要验证部门id是否存在
|
||||
*/
|
||||
export function dept(depts?: string | string[]) {
|
||||
return (target: BaseController, name: string, value: PropertyDescriptor) => {
|
||||
let deptList: string[] = []
|
||||
if (depts) {
|
||||
if (Array.isArray(depts)) {
|
||||
deptList = depts
|
||||
} else {
|
||||
deptList = [depts]
|
||||
}
|
||||
}
|
||||
// @ts-ignore
|
||||
const key = target[name]
|
||||
let deptObj = { depts: deptList }
|
||||
if (RouterMap.decoratedRouters.has(key)) {
|
||||
let objCurrent = RouterMap.decoratedRouters.get(key)
|
||||
Object.assign(objCurrent, deptObj)
|
||||
// @ts-ignore
|
||||
RouterMap.decoratedRouters.set(target[name], objCurrent)
|
||||
} else {
|
||||
// @ts-ignore
|
||||
RouterMap.decoratedRouters.set(target[name], deptObj)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
/**
|
||||
* 单例化一个class
|
||||
* 使用方法:
|
||||
* @singleton
|
||||
* class Test {}
|
||||
* new Test() === new Test() // returns `true`
|
||||
* 也可以不使用 decorator
|
||||
* const TestSingleton = singleton(Test)
|
||||
* new TestSingleton() === new TestSingleton() //returns 'true'
|
||||
*/
|
||||
|
||||
export const SINGLETON_KEY = Symbol()
|
||||
|
||||
export type Singleton<T extends new (...args: any[]) => any> = T & {
|
||||
[SINGLETON_KEY]: T extends new (...args: any[]) => infer I ? I : never
|
||||
}
|
||||
export const singleton = <T extends new (...args: any[]) => any>(classTarget: T) =>
|
||||
new Proxy(classTarget, {
|
||||
construct(target: Singleton<T>, argumentsList, newTarget) {
|
||||
// Skip proxy for children
|
||||
if (target.prototype !== newTarget.prototype) {
|
||||
return Reflect.construct(target, argumentsList, newTarget)
|
||||
}
|
||||
if (!target[SINGLETON_KEY]) {
|
||||
target[SINGLETON_KEY] = Reflect.construct(target, argumentsList, newTarget)
|
||||
}
|
||||
return target[SINGLETON_KEY]
|
||||
},
|
||||
})
|
@ -1,16 +1,16 @@
|
||||
import * as dotenv from 'dotenv'
|
||||
import logger from 'logger/logger'
|
||||
import { RedisClient } from 'redis/RedisClient'
|
||||
const envFile = process.env.NODE_ENV && process.env.NODE_ENV === 'production' ? `.env.production` : '.env.development'
|
||||
dotenv.config({ path: envFile })
|
||||
|
||||
import 'common/Extend'
|
||||
import { GooglePaySvr } from 'service/googlepay.svr'
|
||||
import GooglePurchaseSchedule from 'schedule/googlepurchase.schedule'
|
||||
import { ZRedisClient } from 'zutils'
|
||||
|
||||
async function main() {
|
||||
let opts = { url: process.env.REDIS }
|
||||
new RedisClient(opts)
|
||||
new ZRedisClient(opts)
|
||||
logger.info('REDIS Connected')
|
||||
await new GooglePaySvr().init()
|
||||
logger.info('GooglePaySvr Connected')
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { AsyncQueue, createAsyncQueue } from 'common/AsyncQueue'
|
||||
import { singleton } from 'decorators/singleton'
|
||||
import logger from 'logger/logger'
|
||||
import { UserLog } from 'modules/UserLog'
|
||||
import { singleton, AsyncQueue, createAsyncQueue } from 'zutils'
|
||||
|
||||
@singleton
|
||||
export class LoggerQueue {
|
||||
|
@ -1,5 +1,3 @@
|
||||
import { singleton } from 'decorators/singleton'
|
||||
import { AsyncQueue, createAsyncQueue } from 'common/AsyncQueue'
|
||||
import { DocumentType } from '@typegoose/typegoose'
|
||||
import { PayRecordClass } from 'modules/PayRecord'
|
||||
import logger from 'logger/logger'
|
||||
@ -7,6 +5,7 @@ import { pushTaskToChain } from 'service/chain.svr'
|
||||
import { TransferRecord } from 'modules/TransferRecord'
|
||||
import config from 'config/config'
|
||||
import assert from 'assert'
|
||||
import { AsyncQueue, createAsyncQueue, singleton } from 'zutils'
|
||||
|
||||
/**
|
||||
* let data = {
|
||||
|
@ -1,306 +0,0 @@
|
||||
import { resolveCname } from 'dns'
|
||||
import redis from 'redis'
|
||||
import { promisify } from 'util'
|
||||
import { singleton } from '../decorators/singleton'
|
||||
|
||||
type Callback = (...args: any[]) => void
|
||||
|
||||
@singleton
|
||||
export class RedisClient {
|
||||
public pub: redis.RedisClient
|
||||
public sub: redis.RedisClient
|
||||
|
||||
protected subscribeAsync: any
|
||||
protected unsubscribeAsync: any
|
||||
protected publishAsync: any
|
||||
|
||||
protected subscriptions: { [channel: string]: Callback[] } = {}
|
||||
|
||||
protected smembersAsync: any
|
||||
protected sismemberAsync: any
|
||||
protected hgetAsync: any
|
||||
protected hlenAsync: any
|
||||
protected pubsubAsync: any
|
||||
protected incrAsync: any
|
||||
protected decrAsync: any
|
||||
|
||||
constructor(opts?: redis.ClientOpts) {
|
||||
this.sub = redis.createClient(opts)
|
||||
this.pub = redis.createClient(opts)
|
||||
|
||||
// no listener limit
|
||||
this.sub.setMaxListeners(0)
|
||||
|
||||
// create promisified pub/sub methods.
|
||||
this.subscribeAsync = promisify(this.sub.subscribe).bind(this.sub)
|
||||
this.unsubscribeAsync = promisify(this.sub.unsubscribe).bind(this.sub)
|
||||
|
||||
this.publishAsync = promisify(this.pub.publish).bind(this.pub)
|
||||
|
||||
// create promisified redis methods.
|
||||
this.smembersAsync = promisify(this.pub.smembers).bind(this.pub)
|
||||
this.sismemberAsync = promisify(this.pub.sismember).bind(this.pub)
|
||||
this.hlenAsync = promisify(this.pub.hlen).bind(this.pub)
|
||||
this.hgetAsync = promisify(this.pub.hget).bind(this.pub)
|
||||
this.pubsubAsync = promisify(this.pub.pubsub).bind(this.pub)
|
||||
this.decrAsync = promisify(this.pub.decr).bind(this.pub)
|
||||
this.incrAsync = promisify(this.pub.incr).bind(this.pub)
|
||||
}
|
||||
|
||||
public async subscribe(topic: string, callback: Callback) {
|
||||
if (!this.subscriptions[topic]) {
|
||||
this.subscriptions[topic] = []
|
||||
}
|
||||
|
||||
this.subscriptions[topic].push(callback)
|
||||
|
||||
if (this.sub.listeners('message').length === 0) {
|
||||
this.sub.addListener('message', this.handleSubscription)
|
||||
}
|
||||
|
||||
await this.subscribeAsync(topic)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
public async unsubscribe(topic: string, callback?: Callback) {
|
||||
if (callback) {
|
||||
const index = this.subscriptions[topic].indexOf(callback)
|
||||
this.subscriptions[topic].splice(index, 1)
|
||||
} else {
|
||||
this.subscriptions[topic] = []
|
||||
}
|
||||
|
||||
if (this.subscriptions[topic].length === 0) {
|
||||
await this.unsubscribeAsync(topic)
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
public async publish(topic: string, data: any) {
|
||||
if (data === undefined) {
|
||||
data = false
|
||||
}
|
||||
|
||||
await this.publishAsync(topic, JSON.stringify(data))
|
||||
}
|
||||
|
||||
public async exists(roomId: string): Promise<boolean> {
|
||||
return (await this.pubsubAsync('channels', roomId)).length > 0
|
||||
}
|
||||
|
||||
public async setex(key: string, value: string, seconds: number) {
|
||||
return new Promise(resolve => this.pub.setex(key, seconds, value, resolve))
|
||||
}
|
||||
|
||||
public async expire(key: string, seconds: number) {
|
||||
return new Promise(resolve => this.pub.expire(key, seconds, resolve))
|
||||
}
|
||||
|
||||
public async get(key: string): Promise<string | null> {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.pub.get(key, (err, data: string | null) => {
|
||||
if (err) {
|
||||
return reject(err)
|
||||
}
|
||||
resolve(data)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
public async set(key: string, val: string) {
|
||||
return new Promise(resolve => {
|
||||
this.pub.set(key, val, () => {
|
||||
resolve && resolve('')
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
public async del(roomId: string) {
|
||||
return new Promise(resolve => {
|
||||
this.pub.del(roomId, resolve)
|
||||
})
|
||||
}
|
||||
|
||||
public async sadd(key: string, value: any) {
|
||||
return new Promise(resolve => {
|
||||
this.pub.sadd(key, value, resolve)
|
||||
})
|
||||
}
|
||||
|
||||
public async smembers(key: string): Promise<string[]> {
|
||||
return await this.smembersAsync(key)
|
||||
}
|
||||
|
||||
public async sismember(key: string, field: string): Promise<number> {
|
||||
return await this.sismemberAsync(key, field)
|
||||
}
|
||||
|
||||
public async srem(key: string, value: any) {
|
||||
return new Promise(resolve => {
|
||||
this.pub.srem(key, value, resolve)
|
||||
})
|
||||
}
|
||||
|
||||
public async scard(key: string) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.pub.scard(key, (err, data) => {
|
||||
if (err) {
|
||||
return reject(err)
|
||||
}
|
||||
resolve(data)
|
||||
})
|
||||
})
|
||||
}
|
||||
public async srandmember(key: string) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.pub.srandmember(key, (err, data) => {
|
||||
if (err) {
|
||||
return reject(err)
|
||||
}
|
||||
resolve(data)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
public async sinter(...keys: string[]) {
|
||||
return new Promise<string[]>((resolve, reject) => {
|
||||
this.pub.sinter(...keys, (err, data) => {
|
||||
if (err) {
|
||||
return reject(err)
|
||||
}
|
||||
resolve(data)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
public async zadd(key: string, value: any, member: string) {
|
||||
return new Promise(resolve => {
|
||||
this.pub.zadd(key, value, member, resolve)
|
||||
})
|
||||
}
|
||||
public async zrangebyscore(key: string, min: number, max: number) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.pub.zrangebyscore(key, min, max, 'withscores', (err, data) => {
|
||||
if (err) {
|
||||
return reject(err)
|
||||
}
|
||||
resolve(data)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
public async zcard(key: string) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.pub.zcard(key, (err, data) => {
|
||||
if (err) {
|
||||
return reject(err)
|
||||
}
|
||||
resolve(data)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
public async zcount(key: string, min: number, max: number) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.pub.zcount(key, min, max, (err, data) => {
|
||||
if (err) {
|
||||
return reject(err)
|
||||
}
|
||||
resolve(data)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
public async zrevrank(key: string, member: string) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.pub.zrevrank(key, member, (err, data) => {
|
||||
if (err) {
|
||||
return reject(err)
|
||||
}
|
||||
resolve(data)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
public async zscore(key: string, member: string) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.pub.zscore(key, member, (err, data) => {
|
||||
if (err) {
|
||||
return reject(err)
|
||||
}
|
||||
resolve(data)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
public async zrevrange(key: string, start: number, end: number) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.pub.zrevrange(key, start, end, 'withscores', (err, data) => {
|
||||
if (err) {
|
||||
return reject(err)
|
||||
}
|
||||
resolve(data)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
public async hset(key: string, field: string, value: string) {
|
||||
return new Promise(resolve => {
|
||||
this.pub.hset(key, field, value, resolve)
|
||||
})
|
||||
}
|
||||
|
||||
public async hincrby(key: string, field: string, value: number) {
|
||||
return new Promise(resolve => {
|
||||
this.pub.hincrby(key, field, value, resolve)
|
||||
})
|
||||
}
|
||||
|
||||
public async hget(key: string, field: string) {
|
||||
return await this.hgetAsync(key, field)
|
||||
}
|
||||
|
||||
public async hgetall(key: string) {
|
||||
return new Promise<{ [key: string]: string }>((resolve, reject) => {
|
||||
this.pub.hgetall(key, (err, values) => {
|
||||
if (err) {
|
||||
return reject(err)
|
||||
}
|
||||
resolve(values)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
public async hdel(key: string, field: string) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.pub.hdel(key, field, (err, ok) => {
|
||||
if (err) {
|
||||
return reject(err)
|
||||
}
|
||||
resolve(ok)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
public async hlen(key: string): Promise<number> {
|
||||
return await this.hlenAsync(key)
|
||||
}
|
||||
|
||||
public async incr(key: string): Promise<number> {
|
||||
return await this.incrAsync(key)
|
||||
}
|
||||
|
||||
public async decr(key: string): Promise<number> {
|
||||
return await this.decrAsync(key)
|
||||
}
|
||||
|
||||
protected handleSubscription = (channel: string, message: string) => {
|
||||
if (this.subscriptions[channel]) {
|
||||
for (let i = 0, l = this.subscriptions[channel].length; i < l; i++) {
|
||||
this.subscriptions[channel][i](JSON.parse(message))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
import { singleton } from 'decorators/singleton'
|
||||
import { CodeRecord, CodeStatus } from 'modules/CodeRecord'
|
||||
import * as schedule from 'node-schedule'
|
||||
import { singleton } from 'zutils'
|
||||
|
||||
/**
|
||||
* 定时更新发送邮件验证码的过期状态
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { singleton } from 'decorators/singleton'
|
||||
import logger from 'logger/logger'
|
||||
import { GoogleInApp } from 'modules/GoogleInApp'
|
||||
import * as schedule from 'node-schedule'
|
||||
import { RedisClient } from 'redis/RedisClient'
|
||||
|
||||
import { GooglePaySvr } from 'service/googlepay.svr'
|
||||
import { ZRedisClient, singleton } from 'zutils'
|
||||
|
||||
/**
|
||||
* 定时查询 google voided purchase
|
||||
@ -22,7 +22,7 @@ import { GooglePaySvr } from 'service/googlepay.svr'
|
||||
export default class GooglePurchaseSchedule {
|
||||
async parseAllRecord() {
|
||||
const timeKey = 'google_inapp_voided_check_time'
|
||||
let timeStr = await new RedisClient().get(timeKey)
|
||||
let timeStr = await new ZRedisClient().get(timeKey)
|
||||
let startTime = timeStr ? parseInt(timeStr) : Date.now() - 24 * 60 * 60 * 1000
|
||||
let endTime = Date.now()
|
||||
try {
|
||||
@ -33,7 +33,7 @@ export default class GooglePurchaseSchedule {
|
||||
}
|
||||
const { data } = res
|
||||
await GoogleInApp.parseVoidedRecords(data.voidedPurchases)
|
||||
await new RedisClient().set(timeKey, endTime + '')
|
||||
await new ZRedisClient().set(timeKey, endTime + '')
|
||||
logger.info(`success check google voided purchase:: voidedPurchases: ${data.voidedPurchases?.length || 0}`)
|
||||
} catch (err) {
|
||||
logger.info('error check google voided purchase', err.message || err)
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { singleton } from 'decorators/singleton'
|
||||
import logger from 'logger/logger'
|
||||
import { ReportQueue } from 'modules/ReportQueue'
|
||||
import * as schedule from 'node-schedule'
|
||||
import { reportApplePurchaseResult, reportGooglePurchaseResult } from 'service/game.svr'
|
||||
import { singleton } from 'zutils'
|
||||
|
||||
/**
|
||||
* 定时查询加密货币购买和内购上报失败的记录, 并重试
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { ZError } from 'common/ZError'
|
||||
import { singleton } from 'decorators/singleton'
|
||||
import { shortUuid, uuid } from 'utils/security.util'
|
||||
import { ZError, singleton } from 'zutils'
|
||||
|
||||
export interface IQrData {
|
||||
clientId: string
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { singleton } from 'decorators/singleton'
|
||||
import { NetClient } from 'net/NetClient'
|
||||
import { singleton } from 'zutils'
|
||||
|
||||
export const DEFAULT_VERIFY_HTML = `
|
||||
<h1>Email Verification</h1>
|
||||
|
@ -1,9 +1,10 @@
|
||||
import axios from 'axios'
|
||||
import { singleton } from 'decorators/singleton'
|
||||
|
||||
import { queryPrice } from './alchemy.svr'
|
||||
import * as schedule from 'node-schedule'
|
||||
import logger from 'logger/logger'
|
||||
import { erc20TransferGas, ethTransferGas } from './chain.svr'
|
||||
import { singleton } from 'zutils'
|
||||
|
||||
@singleton
|
||||
export class GasSvr {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { singleton } from 'decorators/singleton'
|
||||
import { androidpublisher_v3, google } from 'googleapis'
|
||||
import logger from 'logger/logger'
|
||||
import { singleton } from 'zutils'
|
||||
|
||||
const PAGEAGE_NAME = 'com.cege.games.release'
|
||||
|
||||
|
@ -7,8 +7,9 @@ import {
|
||||
ProductTypeParameter,
|
||||
SortParameter,
|
||||
} from 'apple/Models'
|
||||
import { singleton } from 'decorators/singleton'
|
||||
|
||||
import logger from 'logger/logger'
|
||||
import { singleton } from 'zutils'
|
||||
|
||||
const env = process.env.NODE_ENV || 'development'
|
||||
const sandbox = env === 'development' ? true : false
|
||||
@ -29,7 +30,7 @@ const apiSandbox = new AppStoreServerAPI(KEY, KEY_ID, ISSUER_ID, APP_BUNDLE_ID,
|
||||
export class IosPaySvr {
|
||||
public async iosPayVerify(orderId: string) {
|
||||
logger.info('iosPayVerify: ', orderId)
|
||||
let response;
|
||||
let response
|
||||
try {
|
||||
response = await apiProd.lookupOrder(orderId)
|
||||
} catch (err) {
|
||||
@ -78,7 +79,7 @@ export class IosPaySvr {
|
||||
}
|
||||
|
||||
public async getTransactionInfo(originalTransactionId: string) {
|
||||
let response;
|
||||
let response
|
||||
try {
|
||||
response = await apiProd.getTransactionInfo(originalTransactionId)
|
||||
} catch (err) {
|
||||
@ -91,8 +92,8 @@ export class IosPaySvr {
|
||||
}
|
||||
}
|
||||
if (!response) {
|
||||
throw new Error('empty transaction info')
|
||||
}
|
||||
throw new Error('empty transaction info')
|
||||
}
|
||||
const transaction = await decodeTransaction(response.signedTransactionInfo)
|
||||
return transaction
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { singleton } from 'decorators/singleton'
|
||||
import { singleton } from 'zutils'
|
||||
|
||||
@singleton
|
||||
export class OrderCacheSvr {
|
||||
|
@ -1,8 +1,8 @@
|
||||
import axios from 'axios'
|
||||
import { singleton } from 'decorators/singleton'
|
||||
import { queryPrice } from './alchemy.svr'
|
||||
import * as schedule from 'node-schedule'
|
||||
import logger from 'logger/logger'
|
||||
import { singleton } from 'zutils'
|
||||
|
||||
@singleton
|
||||
export class PriceSvr {
|
||||
|
Loading…
x
Reference in New Issue
Block a user