feat(transaction): 添加游戏事务系统 | add game transaction system (#381)
- TransactionManager/TransactionContext 事务管理 - MemoryStorage/RedisStorage/MongoStorage 存储实现 - CurrencyOperation/InventoryOperation/TradeOperation 内置操作 - SagaOrchestrator 分布式 Saga 编排 - withTransactions() Room 集成 - 完整中英文文档
This commit is contained in:
@@ -0,0 +1,174 @@
|
||||
/**
|
||||
* @zh Room 事务扩展
|
||||
* @en Room transaction extension
|
||||
*/
|
||||
|
||||
import type {
|
||||
ITransactionStorage,
|
||||
ITransactionContext,
|
||||
TransactionOptions,
|
||||
TransactionResult,
|
||||
} from '../core/types.js'
|
||||
import { TransactionManager } from '../core/TransactionManager.js'
|
||||
|
||||
/**
|
||||
* @zh 事务 Room 配置
|
||||
* @en Transaction Room configuration
|
||||
*/
|
||||
export interface TransactionRoomConfig {
|
||||
/**
|
||||
* @zh 存储实例
|
||||
* @en Storage instance
|
||||
*/
|
||||
storage?: ITransactionStorage
|
||||
|
||||
/**
|
||||
* @zh 默认超时时间(毫秒)
|
||||
* @en Default timeout in milliseconds
|
||||
*/
|
||||
defaultTimeout?: number
|
||||
|
||||
/**
|
||||
* @zh 服务器 ID
|
||||
* @en Server ID
|
||||
*/
|
||||
serverId?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 事务 Room 接口
|
||||
* @en Transaction Room interface
|
||||
*/
|
||||
export interface ITransactionRoom {
|
||||
/**
|
||||
* @zh 事务管理器
|
||||
* @en Transaction manager
|
||||
*/
|
||||
readonly transactions: TransactionManager
|
||||
|
||||
/**
|
||||
* @zh 开始事务
|
||||
* @en Begin transaction
|
||||
*/
|
||||
beginTransaction(options?: TransactionOptions): ITransactionContext
|
||||
|
||||
/**
|
||||
* @zh 执行事务
|
||||
* @en Run transaction
|
||||
*/
|
||||
runTransaction<T = unknown>(
|
||||
builder: (ctx: ITransactionContext) => void | Promise<void>,
|
||||
options?: TransactionOptions
|
||||
): Promise<TransactionResult<T>>
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 创建事务 Room mixin
|
||||
* @en Create transaction Room mixin
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* import { Room } from '@esengine/server'
|
||||
* import { withTransactions, RedisStorage } from '@esengine/transaction'
|
||||
*
|
||||
* class GameRoom extends withTransactions(Room, {
|
||||
* storage: new RedisStorage({ client: redisClient }),
|
||||
* }) {
|
||||
* async handleBuy(itemId: string, player: Player) {
|
||||
* const result = await this.runTransaction((tx) => {
|
||||
* tx.addOperation(new CurrencyOperation({
|
||||
* type: 'deduct',
|
||||
* playerId: player.id,
|
||||
* currency: 'gold',
|
||||
* amount: 100,
|
||||
* }))
|
||||
* })
|
||||
*
|
||||
* if (result.success) {
|
||||
* player.send('buy_success', { itemId })
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export function withTransactions<TBase extends new (...args: any[]) => any>(
|
||||
Base: TBase,
|
||||
config: TransactionRoomConfig = {}
|
||||
): TBase & (new (...args: any[]) => ITransactionRoom) {
|
||||
return class TransactionRoom extends Base implements ITransactionRoom {
|
||||
private _transactionManager: TransactionManager
|
||||
|
||||
constructor(...args: any[]) {
|
||||
super(...args)
|
||||
this._transactionManager = new TransactionManager({
|
||||
storage: config.storage,
|
||||
defaultTimeout: config.defaultTimeout,
|
||||
serverId: config.serverId,
|
||||
})
|
||||
}
|
||||
|
||||
get transactions(): TransactionManager {
|
||||
return this._transactionManager
|
||||
}
|
||||
|
||||
beginTransaction(options?: TransactionOptions): ITransactionContext {
|
||||
return this._transactionManager.begin(options)
|
||||
}
|
||||
|
||||
runTransaction<T = unknown>(
|
||||
builder: (ctx: ITransactionContext) => void | Promise<void>,
|
||||
options?: TransactionOptions
|
||||
): Promise<TransactionResult<T>> {
|
||||
return this._transactionManager.run<T>(builder, options)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 事务 Room 抽象基类
|
||||
* @en Transaction Room abstract base class
|
||||
*
|
||||
* @zh 可以直接继承使用,也可以使用 withTransactions mixin
|
||||
* @en Can be extended directly or use withTransactions mixin
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* class GameRoom extends TransactionRoom {
|
||||
* constructor() {
|
||||
* super({ storage: new RedisStorage({ client: redisClient }) })
|
||||
* }
|
||||
*
|
||||
* async handleTrade(data: TradeData, player: Player) {
|
||||
* const result = await this.runTransaction((tx) => {
|
||||
* // 添加交易操作
|
||||
* })
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export abstract class TransactionRoom implements ITransactionRoom {
|
||||
private _transactionManager: TransactionManager
|
||||
|
||||
constructor(config: TransactionRoomConfig = {}) {
|
||||
this._transactionManager = new TransactionManager({
|
||||
storage: config.storage,
|
||||
defaultTimeout: config.defaultTimeout,
|
||||
serverId: config.serverId,
|
||||
})
|
||||
}
|
||||
|
||||
get transactions(): TransactionManager {
|
||||
return this._transactionManager
|
||||
}
|
||||
|
||||
beginTransaction(options?: TransactionOptions): ITransactionContext {
|
||||
return this._transactionManager.begin(options)
|
||||
}
|
||||
|
||||
runTransaction<T = unknown>(
|
||||
builder: (ctx: ITransactionContext) => void | Promise<void>,
|
||||
options?: TransactionOptions
|
||||
): Promise<TransactionResult<T>> {
|
||||
return this._transactionManager.run<T>(builder, options)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user