From a0d6e3cbc64f1f6c107bfe337d7b675d9562d420 Mon Sep 17 00:00:00 2001 From: Astrian Zheng Date: Sun, 12 Jan 2025 13:07:21 +1100 Subject: [PATCH] feat: add endpoint to update invoice notes with bearer token and input validation --- backend/src/app.ts | 21 +++++++++++++++++++ backend/src/func/index.ts | 4 +++- backend/src/func/updateInvoiceNote.ts | 29 +++++++++++++++++++++++++++ 3 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 backend/src/func/updateInvoiceNote.ts diff --git a/backend/src/app.ts b/backend/src/app.ts index 45a8dad..cd0cb2a 100644 --- a/backend/src/app.ts +++ b/backend/src/app.ts @@ -246,6 +246,27 @@ app.use(route.get('/invoice', async (ctx) => { ctx.body = await Promise.all(invoices.map(async invoice => (await invoice).exportJSON())) })) +app.use(route.put('/invoice/INV-:date(\\d{8})-:suffix(\\d+)/note', async (ctx, invoiceId, suffix) => { + // 请求头验证 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]) + + // 验证 invoiceId 格式 + const date = invoiceId.slice(0, 4) + '-' + invoiceId.slice(4, 6) + '-' + invoiceId.slice(6, 8) + + // 验证必填字段 + if (!ctx.request.body) throw new HttpError(ErrorDescEnum.required_fields_missing, 400, ['note']) + console.log(typeof ctx.request.body.note) + if (typeof ctx.request.body.note !== 'string' && typeof ctx.request.body.note !== 'undefined') throw new HttpError(ErrorDescEnum.invalid_field_format, 400, ['note']) + + // 更新收据备注 + await func.updateInvoiceNote(ctx.prisma, date, suffix, ctx.request.body.note) + ctx.status = 204 +})) + 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/index.ts b/backend/src/func/index.ts index acf1226..4d10718 100644 --- a/backend/src/func/index.ts +++ b/backend/src/func/index.ts @@ -5,6 +5,7 @@ import getSpecificInvoice from "./getSpecificInvoice" import getInvoices from "./getInvoices" import getPayers from "./getPayers" import updatePayer from "./updatePayer" +import updateInvoiceNote from "./updateInvoiceNote" export default { createPayer, @@ -13,5 +14,6 @@ export default { getSpecificInvoice, getInvoices, getPayers, - updatePayer + updatePayer, + updateInvoiceNote } \ No newline at end of file diff --git a/backend/src/func/updateInvoiceNote.ts b/backend/src/func/updateInvoiceNote.ts new file mode 100644 index 0000000..04fa0fd --- /dev/null +++ b/backend/src/func/updateInvoiceNote.ts @@ -0,0 +1,29 @@ +import dayjs from 'dayjs' +import { PrismaClient } from '@prisma/client' +import { ErrorDescEnum, HttpError } from '../classes/HttpError' + +export default async (prisma: PrismaClient, issueDateRaw: string, suffix: string, note?: string) => { + const issueDate = dayjs(issueDateRaw, 'YYYYMMDD').startOf('day').toDate() + const invoice = await prisma.invoice.findFirst({ + where: { + invoice_date: issueDate, + invoice_suffix_code: parseInt(suffix, 10) + } + }) + + if (!invoice) throw new HttpError(ErrorDescEnum.item_not_found, 404, ['invoice']) + + await prisma.invoice.update({ + where: { + invoice_date_invoice_suffix_code: { + invoice_date: issueDate, + invoice_suffix_code: parseInt(suffix, 10) + } + }, + data: { + invoice_note: note + } + }) + + return +} \ No newline at end of file