1interface Order {
2 tenantId: string;
3 orderId: string;
4 customerId: string;
5 status: string;
6 totalCents: number;
7 currency: string;
8 createdAt: Date;
9}
10
11interface OrderRow {
12 tenant_id: string;
13 order_id: string;
14 customer_id: string;
15 status: string;
16 total_cents: string;
17 currency: string;
18 created_at: Date;
19}
20
21class OrderRepository {
22 constructor(private db: ShardedDB) {}
23
24 async create(order: Order): Promise<void> {
25 await this.db.execute(
26 order.tenantId,
27 `INSERT INTO orders (tenant_id, order_id, customer_id, status, total_cents, currency, created_at)
28 VALUES ($1, $2, $3, $4, $5, $6, $7)`,
29 [order.tenantId, order.orderId, order.customerId,
30 order.status, order.totalCents, order.currency, order.createdAt]
31 );
32 }
33
34 async findById(tenantId: string, orderId: string): Promise<Order | null> {
35 const row = await this.db.queryOne<OrderRow>(
36 tenantId,
37 `SELECT tenant_id, order_id, customer_id, status, total_cents, currency, created_at
38 FROM orders WHERE tenant_id = $1 AND order_id = $2`,
39 [tenantId, orderId]
40 );
41 return row ? this.toOrder(row) : null;
42 }
43
44 async listByTenant(tenantId: string, limit = 20, offset = 0): Promise<Order[]> {
45 const rows = await this.db.query<OrderRow>(
46 tenantId,
47 `SELECT tenant_id, order_id, customer_id, status, total_cents, currency, created_at
48 FROM orders WHERE tenant_id = $1
49 ORDER BY created_at DESC LIMIT $2 OFFSET $3`,
50 [tenantId, limit, offset]
51 );
52 return rows.map(this.toOrder);
53 }
54
55 async getGlobalStats(): Promise<{ totalOrders: number; totalRevenue: number }> {
56 return this.db.aggregate<{ count: string; revenue: string }, { totalOrders: number; totalRevenue: number }>(
57 `SELECT COUNT(*) as count, COALESCE(SUM(total_cents), 0) as revenue
58 FROM orders WHERE created_at > NOW() - INTERVAL '30 days'`,
59 undefined,
60 (results) => {
61 let totalOrders = 0;
62 let totalRevenue = 0;
63 for (const result of results) {
64 for (const row of result.data) {
65 totalOrders += parseInt(row.count, 10);
66 totalRevenue += parseInt(row.revenue, 10);
67 }
68 }
69 return { totalOrders, totalRevenue };
70 }
71 );
72 }
73
74 async createInTransaction(tenantId: string, order: Order, items: OrderItem[]): Promise<void> {
75 await this.db.transaction(order.tenantId, async (client) => {
76 await client.query(
77 `INSERT INTO orders (tenant_id, order_id, customer_id, status, total_cents, currency, created_at)
78 VALUES ($1, $2, $3, $4, $5, $6, $7)`,
79 [order.tenantId, order.orderId, order.customerId,
80 order.status, order.totalCents, order.currency, order.createdAt]
81 );
82
83 for (const item of items) {
84 await client.query(
85 `INSERT INTO order_items (tenant_id, order_id, item_id, product_id, quantity, unit_price)
86 VALUES ($1, $2, $3, $4, $5, $6)`,
87 [tenantId, order.orderId, item.itemId, item.productId, item.quantity, item.unitPrice]
88 );
89 }
90 });
91 }
92
93 private toOrder(row: OrderRow): Order {
94 return {
95 tenantId: row.tenant_id,
96 orderId: row.order_id,
97 customerId: row.customer_id,
98 status: row.status,
99 totalCents: parseInt(row.total_cents, 10),
100 currency: row.currency,
101 createdAt: row.created_at,
102 };
103 }
104}
105
106interface OrderItem {
107 itemId: string;
108 productId: string;
109 quantity: number;
110 unitPrice: number;
111}
112