@sfmc-bds/db-server 0.1.0 → 0.2.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/db-tables.js +16 -17
- package/dist/db-tables.js.map +7 -1
- package/dist/domain/bridge.js +54 -77
- package/dist/domain/bridge.js.map +7 -1
- package/dist/domain/economy.d.ts +83 -2
- package/dist/domain/economy.d.ts.map +1 -1
- package/dist/domain/economy.js +292 -281
- package/dist/domain/economy.js.map +7 -1
- package/dist/domain/schema.js +480 -426
- package/dist/domain/schema.js.map +7 -1
- package/dist/domain/transaction.js +32 -80
- package/dist/domain/transaction.js.map +7 -1
- package/dist/env.d.ts.map +1 -1
- package/dist/env.js +85 -84
- package/dist/env.js.map +7 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +218 -240
- package/dist/index.js.map +7 -1
- package/dist/lib/http.js +30 -39
- package/dist/lib/http.js.map +7 -1
- package/dist/lib/idempotency-store.js +34 -47
- package/dist/lib/idempotency-store.js.map +7 -1
- package/dist/lib/idempotency.js +8 -13
- package/dist/lib/idempotency.js.map +7 -1
- package/dist/lib/identifiers.js +18 -14
- package/dist/lib/identifiers.js.map +7 -1
- package/dist/lib/log.d.ts +3 -6
- package/dist/lib/log.d.ts.map +1 -1
- package/dist/lib/log.js +13 -14
- package/dist/lib/log.js.map +7 -1
- package/dist/lib/module-state.d.ts.map +1 -1
- package/dist/lib/module-state.js +32 -21
- package/dist/lib/module-state.js.map +7 -1
- package/dist/lib/order-by.d.ts +17 -0
- package/dist/lib/order-by.d.ts.map +1 -0
- package/dist/lib/order-by.js +24 -0
- package/dist/lib/order-by.js.map +7 -0
- package/dist/lib/runtime.js +16 -31
- package/dist/lib/runtime.js.map +7 -1
- package/dist/lib/sql-helpers.js +9 -27
- package/dist/lib/sql-helpers.js.map +7 -1
- package/dist/lib/sqlite.js +40 -52
- package/dist/lib/sqlite.js.map +7 -1
- package/dist/manifest-loader.d.ts.map +1 -1
- package/dist/manifest-loader.js +154 -195
- package/dist/manifest-loader.js.map +7 -1
- package/dist/manifest.d.ts +3 -7
- package/dist/manifest.d.ts.map +1 -1
- package/dist/manifest.js +76 -115
- package/dist/manifest.js.map +7 -1
- package/dist/module-auth.d.ts +17 -6
- package/dist/module-auth.d.ts.map +1 -1
- package/dist/module-auth.js +93 -107
- package/dist/module-auth.js.map +7 -1
- package/dist/module-runtime-sync.d.ts +37 -0
- package/dist/module-runtime-sync.d.ts.map +1 -0
- package/dist/module-runtime-sync.js +54 -0
- package/dist/module-runtime-sync.js.map +7 -0
- package/dist/permission-gate.d.ts.map +1 -1
- package/dist/permission-gate.js +55 -86
- package/dist/permission-gate.js.map +7 -1
- package/dist/routes/_shared.d.ts +7 -2
- package/dist/routes/_shared.d.ts.map +1 -1
- package/dist/routes/_shared.js +16 -25
- package/dist/routes/_shared.js.map +7 -1
- package/dist/routes/config.d.ts +11 -1
- package/dist/routes/config.d.ts.map +1 -1
- package/dist/routes/config.js +167 -213
- package/dist/routes/config.js.map +7 -1
- package/dist/routes/db-routes.d.ts +6 -10
- package/dist/routes/db-routes.d.ts.map +1 -1
- package/dist/routes/db-routes.js +198 -172
- package/dist/routes/db-routes.js.map +7 -1
- package/dist/routes/health.js +15 -20
- package/dist/routes/health.js.map +7 -1
- package/dist/routes/messages.d.ts.map +1 -1
- package/dist/routes/messages.js +56 -63
- package/dist/routes/messages.js.map +7 -1
- package/dist/routes/module-config-routes.d.ts.map +1 -1
- package/dist/routes/module-config-routes.js +92 -111
- package/dist/routes/module-config-routes.js.map +7 -1
- package/dist/routes/modules.d.ts.map +1 -1
- package/dist/routes/modules.js +82 -88
- package/dist/routes/modules.js.map +7 -1
- package/dist/routes/service-routes.d.ts.map +1 -1
- package/dist/routes/service-routes.js +54 -61
- package/dist/routes/service-routes.js.map +7 -1
- package/dist/runtime.test.js +248 -49
- package/dist/runtime.test.js.map +7 -1
- package/dist/schema-registry.d.ts.map +1 -1
- package/dist/schema-registry.js +119 -160
- package/dist/schema-registry.js.map +7 -1
- package/dist/server.js +69 -72
- package/dist/server.js.map +7 -1
- package/dist/service-registry.d.ts.map +1 -1
- package/dist/service-registry.js +60 -67
- package/dist/service-registry.js.map +7 -1
- package/dist/services/builtin-handlers.d.ts +4 -0
- package/dist/services/builtin-handlers.d.ts.map +1 -1
- package/dist/services/builtin-handlers.js +34 -20
- package/dist/services/builtin-handlers.js.map +7 -1
- package/dist/services/economy-handlers.js +143 -132
- package/dist/services/economy-handlers.js.map +7 -1
- package/dist/tx-runner.d.ts +35 -11
- package/dist/tx-runner.d.ts.map +1 -1
- package/dist/tx-runner.js +293 -250
- package/dist/tx-runner.js.map +7 -1
- package/dist/where.js +71 -94
- package/dist/where.js.map +7 -1
- package/package.json +4 -4
package/dist/domain/economy.js
CHANGED
|
@@ -1,14 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* domain/economy.ts — 经济系统业务核心
|
|
3
|
-
*
|
|
4
|
-
* 提供账户 upsert、通用转账、日常任务奖励与月度统计。
|
|
5
|
-
* land / chat / gui 等通过 service(economy.account.*) 调用本文件。
|
|
6
|
-
*
|
|
7
|
-
* 事务约定(方案 A):
|
|
8
|
-
* - applyEconomySteps / submitDailyTaskSteps — 无 BEGIN/COMMIT,可嵌进外层 db.tx
|
|
9
|
-
* - applyEconomyTransaction / submitDailyTaskTx — 独立调用时自己开事务;
|
|
10
|
-
* alreadyInTx=true 时只跑 steps
|
|
11
|
-
*/
|
|
12
1
|
import { isValidIdempotencyKey } from "../lib/idempotency.js";
|
|
13
2
|
import { sql } from "../lib/sql-helpers.js";
|
|
14
3
|
const TABLE_ACCOUNTS = "sfmc_economy_accounts";
|
|
@@ -17,305 +6,327 @@ const TABLE_IDEMPOTENCY = "sfmc_economy_idempotency";
|
|
|
17
6
|
const TABLE_DAILY = "sfmc_economy_daily_tasks";
|
|
18
7
|
const TABLE_STATS = "sfmc_economy_stats";
|
|
19
8
|
function newTransactionId(now) {
|
|
20
|
-
|
|
9
|
+
return `E${now.toString(36)}${Math.random().toString(36).slice(2, 8)}`;
|
|
21
10
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
};
|
|
11
|
+
function economyResult(row) {
|
|
12
|
+
if (!row) return void 0;
|
|
13
|
+
return {
|
|
14
|
+
playerId: row.player_id,
|
|
15
|
+
playerName: row.player_name_snapshot,
|
|
16
|
+
balance: row.balance,
|
|
17
|
+
version: row.version
|
|
18
|
+
};
|
|
31
19
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
20
|
+
function ensureEconomyAccount(query, playerId, playerName) {
|
|
21
|
+
const now = Date.now();
|
|
22
|
+
query(
|
|
23
|
+
sql(
|
|
24
|
+
`INSERT INTO ${TABLE_ACCOUNTS} (player_id, player_name_snapshot, balance, version, created_at, updated_at)
|
|
36
25
|
VALUES (?, ?, 0, 1, ?, ?)
|
|
37
26
|
ON CONFLICT(player_id) DO UPDATE SET
|
|
38
27
|
player_name_snapshot = excluded.player_name_snapshot,
|
|
39
|
-
updated_at = excluded.updated_at`,
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
28
|
+
updated_at = excluded.updated_at`,
|
|
29
|
+
[String(playerId), String(playerName), now, now]
|
|
30
|
+
)
|
|
31
|
+
);
|
|
32
|
+
const rows = query(sql(`SELECT * FROM ${TABLE_ACCOUNTS} WHERE player_id = ?`, [String(playerId)]));
|
|
33
|
+
if (Array.isArray(rows)) {
|
|
34
|
+
return rows[0];
|
|
35
|
+
}
|
|
36
|
+
return void 0;
|
|
45
37
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
return economyResult(row) ?? null;
|
|
38
|
+
function getEconomyAccount(query, playerId, playerName = "") {
|
|
39
|
+
const row = ensureEconomyAccount(query, playerId, playerName);
|
|
40
|
+
return economyResult(row) ?? null;
|
|
50
41
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
if (
|
|
82
|
-
|
|
83
|
-
}
|
|
84
|
-
// 幂等回放
|
|
85
|
-
if (idempotencyKey) {
|
|
86
|
-
const rows = query(sql(`SELECT response_json FROM ${TABLE_IDEMPOTENCY}
|
|
87
|
-
WHERE actor_id = ? AND idempotency_key = ?`, [actorId, idempotencyKey]));
|
|
88
|
-
const previous = Array.isArray(rows) ? rows[0] : undefined;
|
|
89
|
-
if (previous) {
|
|
90
|
-
return { ...JSON.parse(previous.response_json), replayed: true };
|
|
91
|
-
}
|
|
42
|
+
function applyEconomySteps(query, data) {
|
|
43
|
+
const amount = Number(data.amount);
|
|
44
|
+
const actorId = String(data.actor_id ?? data.actorId ?? "").trim();
|
|
45
|
+
if (!actorId || !Number.isSafeInteger(amount) || amount <= 0) {
|
|
46
|
+
return { ok: false, error: "invalid_amount", status: 400 };
|
|
47
|
+
}
|
|
48
|
+
const sourceId = data.source_player_id ? String(data.source_player_id) : data.sourcePlayerId ? String(data.sourcePlayerId) : null;
|
|
49
|
+
const targetId = data.target_player_id ? String(data.target_player_id) : data.targetPlayerId ? String(data.targetPlayerId) : null;
|
|
50
|
+
const idempotencyKey = String(data.idempotency_key ?? data.idempotencyKey ?? "").trim();
|
|
51
|
+
if (idempotencyKey && !isValidIdempotencyKey(idempotencyKey)) {
|
|
52
|
+
return { ok: false, error: "invalid_idempotency_key", status: 400 };
|
|
53
|
+
}
|
|
54
|
+
if (!sourceId && !targetId) {
|
|
55
|
+
return { ok: false, error: "missing_account", status: 400 };
|
|
56
|
+
}
|
|
57
|
+
if (sourceId && sourceId !== actorId) {
|
|
58
|
+
return { ok: false, error: "forbidden_source", status: 403 };
|
|
59
|
+
}
|
|
60
|
+
if (sourceId && targetId && sourceId === targetId) {
|
|
61
|
+
return { ok: false, error: "same_account", status: 400 };
|
|
62
|
+
}
|
|
63
|
+
if (idempotencyKey) {
|
|
64
|
+
const rows = query(
|
|
65
|
+
sql(
|
|
66
|
+
`SELECT response_json FROM ${TABLE_IDEMPOTENCY}
|
|
67
|
+
WHERE actor_id = ? AND idempotency_key = ?`,
|
|
68
|
+
[actorId, idempotencyKey]
|
|
69
|
+
)
|
|
70
|
+
);
|
|
71
|
+
const previous = Array.isArray(rows) ? rows[0] : void 0;
|
|
72
|
+
if (previous) {
|
|
73
|
+
return { ...JSON.parse(previous.response_json), replayed: true };
|
|
92
74
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
75
|
+
}
|
|
76
|
+
const source = sourceId ? ensureEconomyAccount(query, sourceId, String(data.sourcePlayerName ?? "")) : null;
|
|
77
|
+
const target = targetId ? ensureEconomyAccount(query, targetId, String(data.targetPlayerName ?? "")) : null;
|
|
78
|
+
if (sourceId && !source) {
|
|
79
|
+
return { ok: false, error: "source_not_found", status: 404 };
|
|
80
|
+
}
|
|
81
|
+
if (targetId && !target) {
|
|
82
|
+
return { ok: false, error: "target_not_found", status: 404 };
|
|
83
|
+
}
|
|
84
|
+
if (source && source.balance < amount) {
|
|
85
|
+
return { ok: false, error: "insufficient_funds", balance: source.balance, status: 409 };
|
|
86
|
+
}
|
|
87
|
+
const now = Date.now();
|
|
88
|
+
if (source) {
|
|
89
|
+
query(
|
|
90
|
+
sql(
|
|
91
|
+
`UPDATE ${TABLE_ACCOUNTS}
|
|
107
92
|
SET balance = balance - ?, version = version + 1, updated_at = ?
|
|
108
|
-
WHERE player_id = ? AND balance >= ?`,
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
93
|
+
WHERE player_id = ? AND balance >= ?`,
|
|
94
|
+
[amount, now, source.player_id, amount]
|
|
95
|
+
)
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
if (target) {
|
|
99
|
+
query(
|
|
100
|
+
sql(
|
|
101
|
+
`UPDATE ${TABLE_ACCOUNTS}
|
|
112
102
|
SET balance = balance + ?, version = version + 1, updated_at = ?
|
|
113
|
-
WHERE player_id = ?`,
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
103
|
+
WHERE player_id = ?`,
|
|
104
|
+
[amount, now, target.player_id]
|
|
105
|
+
)
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
const transactionType = String(data.transaction_type ?? data.type ?? "adjustment");
|
|
109
|
+
const referenceType = String(data.reference_type ?? data.referenceType ?? "");
|
|
110
|
+
const referenceId = String(data.reference_id ?? data.referenceId ?? "");
|
|
111
|
+
const reason = String(data.reason ?? data.reasonAlias ?? "");
|
|
112
|
+
const id = newTransactionId(now);
|
|
113
|
+
if (source) {
|
|
114
|
+
query(
|
|
115
|
+
sql(
|
|
116
|
+
`INSERT INTO ${TABLE_TRANSACTIONS}
|
|
122
117
|
(id, transaction_type, actor_id, source_player_id, target_player_id,
|
|
123
118
|
amount, balance_before, balance_after,
|
|
124
119
|
reference_type, reference_id, reason, created_at)
|
|
125
|
-
VALUES (?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?)`,
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
120
|
+
VALUES (?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?)`,
|
|
121
|
+
[
|
|
122
|
+
id + "-dr",
|
|
123
|
+
transactionType + ".dr",
|
|
124
|
+
actorId,
|
|
125
|
+
sourceId,
|
|
126
|
+
amount,
|
|
127
|
+
source.balance,
|
|
128
|
+
source.balance - amount,
|
|
129
|
+
referenceType,
|
|
130
|
+
referenceId,
|
|
131
|
+
reason,
|
|
132
|
+
now
|
|
133
|
+
]
|
|
134
|
+
)
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
if (target) {
|
|
138
|
+
query(
|
|
139
|
+
sql(
|
|
140
|
+
`INSERT INTO ${TABLE_TRANSACTIONS}
|
|
141
141
|
(id, transaction_type, actor_id, source_player_id, target_player_id,
|
|
142
142
|
amount, balance_before, balance_after,
|
|
143
143
|
reference_type, reference_id, reason, created_at)
|
|
144
|
-
VALUES (?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
144
|
+
VALUES (?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
145
|
+
[
|
|
146
|
+
id + "-cr",
|
|
147
|
+
transactionType + ".cr",
|
|
148
|
+
actorId,
|
|
149
|
+
targetId,
|
|
150
|
+
amount,
|
|
151
|
+
target.balance,
|
|
152
|
+
target.balance + amount,
|
|
153
|
+
referenceType,
|
|
154
|
+
referenceId,
|
|
155
|
+
reason,
|
|
156
|
+
now
|
|
157
|
+
]
|
|
158
|
+
)
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
const sourceView = source ? economyResult(ensureEconomyAccount(query, source.player_id, "")) : void 0;
|
|
162
|
+
const targetView = target ? economyResult(ensureEconomyAccount(query, target.player_id, "")) : void 0;
|
|
163
|
+
const response = {
|
|
164
|
+
ok: true,
|
|
165
|
+
transactionId: id,
|
|
166
|
+
source: sourceView ? { ...sourceView, balanceBefore: source.balance, balanceAfter: source.balance - amount } : null,
|
|
167
|
+
target: targetView ? { ...targetView, balanceBefore: target.balance, balanceAfter: target.balance + amount } : null
|
|
168
|
+
};
|
|
169
|
+
if (idempotencyKey) {
|
|
170
|
+
query(
|
|
171
|
+
sql(
|
|
172
|
+
`INSERT INTO ${TABLE_IDEMPOTENCY}
|
|
172
173
|
(actor_id, idempotency_key, transaction_id, response_json, created_at)
|
|
173
|
-
VALUES (?, ?, ?, ?, ?)`,
|
|
174
|
-
|
|
175
|
-
|
|
174
|
+
VALUES (?, ?, ?, ?, ?)`,
|
|
175
|
+
[actorId, idempotencyKey, id, JSON.stringify(response), now]
|
|
176
|
+
)
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
return response;
|
|
176
180
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
181
|
+
function applyEconomyTransaction(query, db, data, opts) {
|
|
182
|
+
if (opts?.alreadyInTx) {
|
|
183
|
+
return applyEconomySteps(query, data);
|
|
184
|
+
}
|
|
185
|
+
db.exec("BEGIN IMMEDIATE");
|
|
186
|
+
try {
|
|
187
|
+
const result = applyEconomySteps(query, data);
|
|
188
|
+
if (!result.ok) {
|
|
189
|
+
db.exec("ROLLBACK");
|
|
190
|
+
return result;
|
|
183
191
|
}
|
|
184
|
-
db.exec("
|
|
192
|
+
db.exec("COMMIT");
|
|
193
|
+
return result;
|
|
194
|
+
} catch (error) {
|
|
185
195
|
try {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
db.exec("ROLLBACK");
|
|
189
|
-
return result;
|
|
190
|
-
}
|
|
191
|
-
db.exec("COMMIT");
|
|
192
|
-
return result;
|
|
193
|
-
}
|
|
194
|
-
catch (error) {
|
|
195
|
-
try {
|
|
196
|
-
db.exec("ROLLBACK");
|
|
197
|
-
}
|
|
198
|
-
catch {
|
|
199
|
-
/* ignore */
|
|
200
|
-
}
|
|
201
|
-
return {
|
|
202
|
-
ok: false,
|
|
203
|
-
error: error.message || "economy_transaction_failed",
|
|
204
|
-
status: 500,
|
|
205
|
-
};
|
|
196
|
+
db.exec("ROLLBACK");
|
|
197
|
+
} catch {
|
|
206
198
|
}
|
|
199
|
+
return {
|
|
200
|
+
ok: false,
|
|
201
|
+
error: error.message || "economy_transaction_failed",
|
|
202
|
+
status: 500
|
|
203
|
+
};
|
|
204
|
+
}
|
|
207
205
|
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
return Array.isArray(rows) ? rows : [];
|
|
206
|
+
function listDailyTasks(query, filter) {
|
|
207
|
+
const status = filter?.status ?? "active";
|
|
208
|
+
const now = Date.now();
|
|
209
|
+
if (filter?.includeExpired) {
|
|
210
|
+
const rows2 = query(sql(`SELECT * FROM ${TABLE_DAILY} WHERE status = ?`, [status]));
|
|
211
|
+
return Array.isArray(rows2) ? rows2 : [];
|
|
212
|
+
}
|
|
213
|
+
const rows = query(sql(`SELECT * FROM ${TABLE_DAILY} WHERE status = ? AND expires_at > ?`, [status, now]));
|
|
214
|
+
return Array.isArray(rows) ? rows : [];
|
|
218
215
|
}
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
216
|
+
function submitDailyTaskSteps(query, data) {
|
|
217
|
+
const actorId = data.actorId;
|
|
218
|
+
const taskId = data.taskId;
|
|
219
|
+
const quantity = data.quantity;
|
|
220
|
+
const rows = query(
|
|
221
|
+
sql(
|
|
222
|
+
`SELECT * FROM ${TABLE_DAILY}
|
|
223
|
+
WHERE id = ? AND status = 'active' AND expires_at > ?`,
|
|
224
|
+
[taskId, Date.now()]
|
|
225
|
+
)
|
|
226
|
+
);
|
|
227
|
+
const task = rows[0];
|
|
228
|
+
if (!task) {
|
|
229
|
+
return { ok: false, error: "task_not_found_or_expired", status: 404 };
|
|
230
|
+
}
|
|
231
|
+
const targetQty = Number(task.target_qty ?? 0);
|
|
232
|
+
const filledQty = Number(task.filled_qty ?? 0);
|
|
233
|
+
const remaining = targetQty - filledQty;
|
|
234
|
+
if (remaining < quantity) {
|
|
235
|
+
return { ok: false, error: "task_quota_exceeded", status: 409, extra: { remaining } };
|
|
236
|
+
}
|
|
237
|
+
const reward = Number(task.unit_reward) * quantity;
|
|
238
|
+
query(sql(`UPDATE ${TABLE_DAILY} SET filled_qty = filled_qty + ? WHERE id = ?`, [quantity, taskId]));
|
|
239
|
+
const result = applyEconomySteps(query, {
|
|
240
|
+
actor_id: actorId,
|
|
241
|
+
transaction_type: "daily_task.reward",
|
|
242
|
+
target_player_id: actorId,
|
|
243
|
+
amount: reward,
|
|
244
|
+
reference_type: "daily_task",
|
|
245
|
+
reference_id: taskId,
|
|
246
|
+
reason: `\u63D0\u4EA4\u4EFB\u52A1 ${task.item_type}*${quantity}`
|
|
247
|
+
});
|
|
248
|
+
if (!result.ok) {
|
|
249
|
+
return { ok: false, error: result.error, status: result.status };
|
|
250
|
+
}
|
|
251
|
+
const tgtFinal = economyResult(ensureEconomyAccount(query, actorId, ""));
|
|
252
|
+
return {
|
|
253
|
+
ok: true,
|
|
254
|
+
data: {
|
|
255
|
+
transactionId: result.transactionId,
|
|
256
|
+
reward,
|
|
257
|
+
balance: tgtFinal?.balance ?? 0,
|
|
258
|
+
balanceVersion: tgtFinal?.version ?? 0
|
|
249
259
|
}
|
|
250
|
-
|
|
251
|
-
return {
|
|
252
|
-
ok: true,
|
|
253
|
-
data: {
|
|
254
|
-
transactionId: result.transactionId,
|
|
255
|
-
reward,
|
|
256
|
-
balance: tgtFinal?.balance ?? 0,
|
|
257
|
-
balanceVersion: tgtFinal?.version ?? 0,
|
|
258
|
-
},
|
|
259
|
-
};
|
|
260
|
+
};
|
|
260
261
|
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
262
|
+
function submitDailyTaskTx(query, db, data, opts) {
|
|
263
|
+
if (opts?.alreadyInTx) {
|
|
264
|
+
return submitDailyTaskSteps(query, data);
|
|
265
|
+
}
|
|
266
|
+
db.exec("BEGIN IMMEDIATE");
|
|
267
|
+
try {
|
|
268
|
+
const result = submitDailyTaskSteps(query, data);
|
|
269
|
+
if (!result.ok) {
|
|
270
|
+
db.exec("ROLLBACK");
|
|
271
|
+
return result;
|
|
265
272
|
}
|
|
266
|
-
db.exec("
|
|
273
|
+
db.exec("COMMIT");
|
|
274
|
+
return result;
|
|
275
|
+
} catch (error) {
|
|
267
276
|
try {
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
db.exec("ROLLBACK");
|
|
271
|
-
return result;
|
|
272
|
-
}
|
|
273
|
-
db.exec("COMMIT");
|
|
274
|
-
return result;
|
|
275
|
-
}
|
|
276
|
-
catch (error) {
|
|
277
|
-
try {
|
|
278
|
-
db.exec("ROLLBACK");
|
|
279
|
-
}
|
|
280
|
-
catch {
|
|
281
|
-
/* ignore */
|
|
282
|
-
}
|
|
283
|
-
return {
|
|
284
|
-
ok: false,
|
|
285
|
-
error: error.message || "task_submit_failed",
|
|
286
|
-
status: 500,
|
|
287
|
-
};
|
|
277
|
+
db.exec("ROLLBACK");
|
|
278
|
+
} catch {
|
|
288
279
|
}
|
|
280
|
+
return {
|
|
281
|
+
ok: false,
|
|
282
|
+
error: error.message || "task_submit_failed",
|
|
283
|
+
status: 500
|
|
284
|
+
};
|
|
285
|
+
}
|
|
289
286
|
}
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
const row = cached[0];
|
|
297
|
-
return {
|
|
298
|
-
id: String(row.id ?? id),
|
|
299
|
-
total_issued: Number(row.total_issued ?? 0),
|
|
300
|
-
total_destroyed: Number(row.total_destroyed ?? 0),
|
|
301
|
-
total_supply: Number(row.total_supply ?? 0),
|
|
302
|
-
active_accounts: Number(row.active_accounts ?? 0),
|
|
303
|
-
};
|
|
304
|
-
}
|
|
305
|
-
const supplyRows = query(sql(`SELECT COALESCE(SUM(balance),0) AS total_supply, COUNT(*) AS active_accounts FROM ${TABLE_ACCOUNTS}`));
|
|
306
|
-
const supply = Array.isArray(supplyRows) ? supplyRows[0] : {};
|
|
307
|
-
const issuedRows = query(sql(`SELECT COALESCE(SUM(amount),0) AS total FROM ${TABLE_TRANSACTIONS} WHERE target_player_id IS NOT NULL`));
|
|
308
|
-
const destroyedRows = query(sql(`SELECT COALESCE(SUM(amount),0) AS total FROM ${TABLE_TRANSACTIONS} WHERE source_player_id IS NOT NULL`));
|
|
309
|
-
const issued = Array.isArray(issuedRows) ? Number(issuedRows[0].total ?? 0) : 0;
|
|
310
|
-
const destroyed = Array.isArray(destroyedRows)
|
|
311
|
-
? Number(destroyedRows[0].total ?? 0)
|
|
312
|
-
: 0;
|
|
287
|
+
function monthlyEconomyStats(query) {
|
|
288
|
+
const now = /* @__PURE__ */ new Date();
|
|
289
|
+
const id = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}`;
|
|
290
|
+
const cached = query(sql(`SELECT * FROM ${TABLE_STATS} WHERE id = ? OR id = ?`, ["global", id]));
|
|
291
|
+
if (Array.isArray(cached) && cached.length > 0) {
|
|
292
|
+
const row = cached[0];
|
|
313
293
|
return {
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
294
|
+
id: String(row.id ?? id),
|
|
295
|
+
total_issued: Number(row.total_issued ?? 0),
|
|
296
|
+
total_destroyed: Number(row.total_destroyed ?? 0),
|
|
297
|
+
total_supply: Number(row.total_supply ?? 0),
|
|
298
|
+
active_accounts: Number(row.active_accounts ?? 0)
|
|
319
299
|
};
|
|
300
|
+
}
|
|
301
|
+
const supplyRows = query(
|
|
302
|
+
sql(`SELECT COALESCE(SUM(balance),0) AS total_supply, COUNT(*) AS active_accounts FROM ${TABLE_ACCOUNTS}`)
|
|
303
|
+
);
|
|
304
|
+
const supply = Array.isArray(supplyRows) ? supplyRows[0] : {};
|
|
305
|
+
const issuedRows = query(
|
|
306
|
+
sql(`SELECT COALESCE(SUM(amount),0) AS total FROM ${TABLE_TRANSACTIONS} WHERE target_player_id IS NOT NULL`)
|
|
307
|
+
);
|
|
308
|
+
const destroyedRows = query(
|
|
309
|
+
sql(`SELECT COALESCE(SUM(amount),0) AS total FROM ${TABLE_TRANSACTIONS} WHERE source_player_id IS NOT NULL`)
|
|
310
|
+
);
|
|
311
|
+
const issued = Array.isArray(issuedRows) ? Number(issuedRows[0].total ?? 0) : 0;
|
|
312
|
+
const destroyed = Array.isArray(destroyedRows) ? Number(destroyedRows[0].total ?? 0) : 0;
|
|
313
|
+
return {
|
|
314
|
+
id,
|
|
315
|
+
total_issued: issued,
|
|
316
|
+
total_destroyed: destroyed,
|
|
317
|
+
total_supply: Number(supply.total_supply ?? 0),
|
|
318
|
+
active_accounts: Number(supply.active_accounts ?? 0)
|
|
319
|
+
};
|
|
320
320
|
}
|
|
321
|
-
|
|
321
|
+
export {
|
|
322
|
+
applyEconomySteps,
|
|
323
|
+
applyEconomyTransaction,
|
|
324
|
+
economyResult,
|
|
325
|
+
ensureEconomyAccount,
|
|
326
|
+
getEconomyAccount,
|
|
327
|
+
listDailyTasks,
|
|
328
|
+
monthlyEconomyStats,
|
|
329
|
+
submitDailyTaskSteps,
|
|
330
|
+
submitDailyTaskTx
|
|
331
|
+
};
|
|
332
|
+
//# sourceMappingURL=economy.js.map
|