feat: implement auction bidding and resource adjustment commands

- Added `adjustGeneralResources` command to handle resource adjustments for generals.
- Introduced `patchGeneral` command for updating general attributes.
- Created `auctionBid` command to facilitate bidding in auctions with validation.
- Refactored database interactions to use transactions for auction bids and resource adjustments.
- Enhanced error handling for auction and resource operations.
- Updated command handling in the turn daemon to support new commands.
- Introduced `AuctionBidder` interface and implementation for managing auction bids.
- Improved overall structure and readability of auction-related code.
This commit is contained in:
2026-01-24 04:25:04 +00:00
parent cbee0d4c20
commit 52be3b40ef
10 changed files with 903 additions and 329 deletions
@@ -42,6 +42,9 @@ type TurnDaemonEventEnvelope = {
event: TurnDaemonEvent;
};
const isRecord = (value: unknown): value is Record<string, unknown> =>
Boolean(value && typeof value === 'object' && !Array.isArray(value));
const parseCommandEnvelope = (raw: string): TurnDaemonCommandEnvelope | null => {
try {
const parsed = JSON.parse(raw) as Partial<TurnDaemonCommandEnvelope>;
@@ -294,6 +297,77 @@ const normalizeCommand = (envelope: TurnDaemonCommandEnvelope): TurnDaemonComman
expectedUpdatedAt: typeof command.expectedUpdatedAt === 'string' ? command.expectedUpdatedAt : undefined,
};
}
case 'adjustGeneralResources': {
if (!Array.isArray(command.adjustments)) {
return null;
}
const adjustments = command.adjustments
.filter((entry) => entry && typeof entry.generalId === 'number')
.map((entry) => ({
generalId: entry.generalId,
goldDelta: typeof entry.goldDelta === 'number' ? entry.goldDelta : undefined,
riceDelta: typeof entry.riceDelta === 'number' ? entry.riceDelta : undefined,
}))
.filter((entry) => entry.goldDelta !== undefined || entry.riceDelta !== undefined);
if (adjustments.length === 0) {
return null;
}
return {
type: 'adjustGeneralResources',
requestId: envelope.requestId,
reason: typeof command.reason === 'string' ? command.reason : undefined,
adjustments,
};
}
case 'patchGeneral': {
if (typeof command.generalId !== 'number' || !command.patch || typeof command.patch !== 'object') {
return null;
}
return {
type: 'patchGeneral',
requestId: envelope.requestId,
generalId: command.generalId,
patch: {
meta: isRecord(command.patch.meta) ? command.patch.meta : undefined,
turnTime: typeof command.patch.turnTime === 'string' ? command.patch.turnTime : undefined,
stats: isRecord(command.patch.stats)
? {
leadership:
typeof command.patch.stats.leadership === 'number'
? command.patch.stats.leadership
: undefined,
strength:
typeof command.patch.stats.strength === 'number'
? command.patch.stats.strength
: undefined,
intelligence:
typeof command.patch.stats.intelligence === 'number'
? command.patch.stats.intelligence
: undefined,
}
: undefined,
specialWar: typeof command.patch.specialWar === 'string' ? command.patch.specialWar : undefined,
},
};
}
case 'auctionBid': {
if (
typeof command.auctionId !== 'number' ||
typeof command.generalId !== 'number' ||
typeof command.amount !== 'number'
) {
return null;
}
return {
type: 'auctionBid',
requestId: envelope.requestId,
auctionId: command.auctionId,
generalId: command.generalId,
amount: command.amount,
tryExtendCloseDate:
typeof command.tryExtendCloseDate === 'boolean' ? command.tryExtendCloseDate : undefined,
};
}
case 'getStatus': {
const requestId = typeof command.requestId === 'string' ? command.requestId : envelope.requestId;
return { type: 'getStatus', requestId };