From 940468f23bffb946f047f33547495ed30637d2a6 Mon Sep 17 00:00:00 2001 From: Astrian Zheng Date: Sun, 12 Jan 2025 12:43:19 +1100 Subject: [PATCH] feat: add endpoint for retrieving paginated invoices with bearer token validation --- backend/src/app.ts | 22 +++++++++++++++++++ backend/src/func/getInvoices.ts | 38 +++++++++++++++++++++++++++++++++ backend/src/func/index.ts | 4 +++- 3 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 backend/src/func/getInvoices.ts diff --git a/backend/src/app.ts b/backend/src/app.ts index 219bab5..228a6e7 100644 --- a/backend/src/app.ts +++ b/backend/src/app.ts @@ -160,6 +160,28 @@ app.use(route.get('/invoice/INV-:date(\\d{8})-:suffix(\\d+)', async (ctx, invoic ctx.body = invoice.exportJSON() })) +app.use(route.get('/invoice', async (ctx) => { + // 请求头验证 bearer token + const bearerToken = ctx.request.headers['authorization']?.split(' ') + if (!bearerToken) throw new HttpError(ErrorDescEnum.unauthorized, 401) + if (bearerToken[0] !== 'Bearer') throw new HttpError(ErrorDescEnum.unauthorized, 401) + if (!bearerToken[1]) throw new HttpError(ErrorDescEnum.unauthorized, 401) + await func.verifyBearerToken(bearerToken[1]) + + // 获取 pager 和 limit + const pageQuery = ctx.request.query.page + if (Array.isArray(pageQuery)) throw new HttpError(ErrorDescEnum.invalid_field_format, 400, ['page']) + const page = pageQuery ? parseInt(pageQuery) : 1 + const limitQuery = ctx.request.query.limit + if (Array.isArray(limitQuery)) throw new HttpError(ErrorDescEnum.invalid_field_format, 400, ['limit']) + const limit = limitQuery ? parseInt(limitQuery) : 10 + if (page <= 0 || limit < 5) throw new HttpError(ErrorDescEnum.invalid_field_format, 400, ['page', 'limit']) + + // 获取收据 + const invoices = await func.getInvoices(ctx.prisma, page, limit) + ctx.body = await Promise.all(invoices.map(async invoice => (await invoice).exportJSON())) +})) + const port = parseInt(process.env.PORT ?? '3000') app.listen(port, () => { console.log(`Server is running at http://localhost:${port}`) diff --git a/backend/src/func/getInvoices.ts b/backend/src/func/getInvoices.ts new file mode 100644 index 0000000..f426202 --- /dev/null +++ b/backend/src/func/getInvoices.ts @@ -0,0 +1,38 @@ +import { PrismaClient } from '@prisma/client' +import Invoice from '../classes/Invoice' +import InvoiceItem from '../classes/InvoiceItem' + +export default async function getInvoices(prisma: PrismaClient, page: number, limit: number) { + const invoices = await prisma.invoice.findMany({ + skip: (page - 1) * limit, + take: limit, + orderBy: [{ + invoice_date: 'desc' + }, { + invoice_suffix_code: 'desc' + }] + }) + + const result = invoices.map(async invoice => { + const invoiceItemsRaw = await prisma.invoiceItem.findMany({ + where: { + invoice_suffix_code: invoice.invoice_suffix_code, + invoice_date: invoice.invoice_date + } + }) + + const invoiceItems = invoiceItemsRaw.map(item => { + return new InvoiceItem(item.item_id, item.item_description, item.item_quantity, item.item_unit, item.item_unit_price) + }) + + const payer = { + id: invoice.payer_id, + name: invoice.invoice_payer_name, + address: invoice.invoice_payer_address, + abn: invoice.invoice_payer_abn ?? undefined + } + return new Invoice(invoice.invoice_suffix_code, invoice.invoice_date, [invoice.invoice_billing_period_start, invoice.invoice_billing_period_end], invoiceItems, invoice.invoice_due_date, payer) + }) + + return result +} \ No newline at end of file diff --git a/backend/src/func/index.ts b/backend/src/func/index.ts index bde06ae..3e7b85a 100644 --- a/backend/src/func/index.ts +++ b/backend/src/func/index.ts @@ -2,10 +2,12 @@ import createPayer from "./createPayer" import issueInvoice from "./issueInvoice" import verifyBearerToken from "./verifyBearerToken" import getSpecificInvoice from "./getSpecificInvoice" +import getInvoices from "./getInvoices" export default { createPayer, issueInvoice, verifyBearerToken, - getSpecificInvoice + getSpecificInvoice, + getInvoices, } \ No newline at end of file