diff --git a/package.json b/package.json index 6a4fd0c..29f958c 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "build": "tsc", "start": "node src/dist/bin/www.js", "dev": "tsc && node src/dist/bin/www.js", - "swagger": "node src/dist/swagger.js" + "swagger": "node src/dist/swagger/index.js" }, "keywords": [], "author": "", diff --git a/src/controller/productController.ts b/src/controller/productController.ts index 38a1fc5..cec902d 100644 --- a/src/controller/productController.ts +++ b/src/controller/productController.ts @@ -5,6 +5,7 @@ import { ResponseObject } from '../utils/responseObject'; import { ProductService } from '../service/productService'; import { NewProductDto } from '../dto/newProductDto'; import { NewProductVo } from '../vo/newProductVo'; +import { TCreateProductsReq } from '../types/product.type'; class ProductController extends BaseController { private readonly productService = new ProductService(); @@ -18,7 +19,9 @@ class ProductController extends BaseController { ); }; - public createProducts = async (req: Request): Promise => { + public createProducts = async ( + req: TCreateProductsReq, + ): Promise => { const newProductsDto = new NewProductDto(req); const products = await this.productService.createProducts(newProductsDto); return this.formatResponse( diff --git a/src/dto/newProductDto.ts b/src/dto/newProductDto.ts index 094eb6c..d606ddf 100644 --- a/src/dto/newProductDto.ts +++ b/src/dto/newProductDto.ts @@ -1,14 +1,17 @@ -import { Request } from 'express'; import { IProduct } from '../models/product'; +import { TCreateProductsReq } from '../types/product.type'; export class NewProductDto { - private readonly products: [IProduct]; + private readonly products: IProduct[]; - get getNewProducts(): [IProduct] { + get getNewProducts(): IProduct[] { return this.products; } - constructor(req: Request) { - this.products = req.body.products; + constructor(req: TCreateProductsReq) { + this.products = req.body.products.map((product) => ({ + ...product, + soldAmount: 0, + })); } } diff --git a/src/models/product.ts b/src/models/product.ts index e6feffc..6779146 100644 --- a/src/models/product.ts +++ b/src/models/product.ts @@ -30,6 +30,7 @@ export interface IProduct extends Document, ITimestamp { cancelPolicies?: [string]; certificates?: [string]; comments?: [string]; + soldAmount: number; } const schema = new Schema( @@ -79,6 +80,10 @@ const schema = new Schema( message: '總數量錯誤,請確認 plans 內部數量', }, }, + soldAmount: { + type: Number, + default: 0, + }, plans: { type: [ { @@ -122,7 +127,7 @@ const schema = new Schema( required: true, validate: { validator: function (sellStartAt: Date) { - return moment().isBefore(sellStartAt, 'day'); + return moment().isBefore(moment(sellStartAt), 'day'); }, message: '販賣開始時間必須晚於現在時間至少一天', }, diff --git a/src/swagger-output.json b/src/swagger-output.json index cedf63c..9a5955c 100644 --- a/src/swagger-output.json +++ b/src/swagger-output.json @@ -338,7 +338,12 @@ "type": "object", "properties": {} } - } + }, + "required": [ + "status", + "message", + "data" + ] }, "Error6202": { "type": "object", @@ -350,52 +355,63 @@ "message": { "type": "string", "example": "密碼錯誤" - }, - "data": { - "type": "object", - "properties": {} } - } + }, + "required": [ + "status", + "message" + ] }, "ErrorToken": { "type": "object", "properties": { "status": { "type": "string", - "example": "false" + "example": "6303" }, "message": { "type": "string", - "example": "你尚未登入" - }, - "error": { - "type": "object", - "properties": { - "name": { - "type": "string", - "example": "40300" - } - } + "example": "尚未登入" } - } + }, + "required": [ + "status", + "message" + ] }, "Error404": { "type": "object", "properties": { + "status": { + "type": "number", + "example": 404 + }, "message": { "type": "string", "example": "找不到頁面" } - } + }, + "required": [ + "status", + "message" + ] }, "Error500": { "type": "object", "properties": { + "status": { + "type": "number", + "example": 500 + }, "message": { "type": "string", "example": "系統錯誤,請稍後再試" } - } + }, + "required": [ + "status", + "message" + ] }, "SignUpForm": { "type": "object", @@ -489,7 +505,12 @@ } } } - } + }, + "required": [ + "status", + "message", + "data" + ] }, "UserDetail": { "type": "object", @@ -536,7 +557,12 @@ } } } - } + }, + "required": [ + "status", + "message", + "data" + ] }, "UpdateUserDetail": { "type": "object", @@ -553,40 +579,65 @@ "type": "object", "properties": {} } - } + }, + "required": [ + "status", + "message", + "data" + ] }, "RegisterEmailSuccess": { "type": "object", "properties": { "status": { "type": "string", - "example": "success" + "example": "6000" }, "message": { "type": "string", "example": "請至信箱確認是否收到驗證信" } - } + }, + "required": [ + "status", + "message" + ] }, "RegisterEmailError": { "type": "object", "properties": { + "status": { + "type": "string", + "example": "6504" + }, "message": { "type": "string", "example": "請稍後重試或聯絡管理員" } - } + }, + "required": [ + "status", + "message" + ] }, "ValidateEmailError": { "type": "object", "properties": { + "status": { + "type": "string", + "example": "6505" + }, "message": { "type": "string", "example": "信箱驗證失敗" } - } + }, + "required": [ + "status", + "message" + ] }, - "CustomCreateProductsSuccess": { + "CreateProductsSuccess": { "type": "object", "properties": { "status": { @@ -633,6 +684,10 @@ "type": "number", "example": 100 }, + "soldAmount": { + "type": "number", + "example": 0 + }, "plans": { "type": "array", "items": { @@ -671,19 +726,19 @@ }, "sellEndAt": { "type": "string", - "example": "2024-05-09T12:19:42.178Z" + "example": "2024-05-09T15:42:53.661Z" }, "sellStartAt": { "type": "string", - "example": "2024-05-09T10:19:42.180Z" + "example": "2024-05-09T13:42:53.662Z" }, "endAt": { "type": "string", - "example": "2024-05-09T16:19:42.180Z" + "example": "2024-05-09T19:42:53.662Z" }, "startAt": { "type": "string", - "example": "2024-05-09T14:19:42.180Z" + "example": "2024-05-09T17:42:53.662Z" }, "tags": { "type": "array", @@ -768,6 +823,7 @@ "theater", "price", "amount", + "soldAmount", "introduction", "isLaunched", "isPublic", @@ -788,6 +844,23 @@ "data" ] }, + "CreateProductsError": { + "type": "object", + "properties": { + "status": { + "type": "string", + "example": "6213" + }, + "message": { + "type": "string", + "example": "新增錯誤" + } + }, + "required": [ + "status", + "message" + ] + }, "CustomCreateProductsObj": { "type": "object", "required": [ @@ -806,6 +879,7 @@ "theater", "price", "amount", + "soldAmount", "startAt", "endAt", "sellStartAt", @@ -881,6 +955,12 @@ "min": 0, "description": "票券總量,不得低於最大人數方案。" }, + "soldAmount": { + "type": "number", + "example": 0, + "min": 0, + "description": "已銷售數量" + }, "plans": { "type": "array", "items": { @@ -937,26 +1017,26 @@ }, "sellEndAt": { "type": "Date", - "example": "2024-05-09T12:19:42.178Z", - "min": "2024-05-08T11:19:42.180Z", + "example": "2024-05-09T15:42:53.661Z", + "min": "2024-05-08T14:42:53.662Z", "description": "販賣結束時間,必須晚於販賣開始時間至少一個小時" }, "sellStartAt": { "type": "Date", - "example": "2024-05-09T10:19:42.180Z", - "min": "2024-05-08T10:19:42.180Z", + "example": "2024-05-09T13:42:53.662Z", + "min": "2024-05-08T13:42:53.662Z", "description": "販賣開始時間,必須晚於現在時間至少一天" }, "endAt": { "type": "Date", - "example": "2024-05-09T16:19:42.180Z", - "min": "2024-05-08T13:19:42.180Z", + "example": "2024-05-09T19:42:53.662Z", + "min": "2024-05-08T16:42:53.662Z", "description": "活動結束時間,必須晚於活動開始時間至少一個小時" }, "startAt": { "type": "Date", - "example": "2024-05-09T14:19:42.180Z", - "min": "2024-05-08T12:19:42.180Z", + "example": "2024-05-09T17:42:53.662Z", + "min": "2024-05-08T15:42:53.663Z", "description": "活動開始時間,必須晚於販售結束時間至少一個小時" }, "tags": { diff --git a/src/swagger/definition/product.ts b/src/swagger/definition/product.ts index 36fcc1c..f6c0142 100644 --- a/src/swagger/definition/product.ts +++ b/src/swagger/definition/product.ts @@ -16,6 +16,7 @@ const CustomProduct = { $theater: '信義威秀', $price: 1100, $amount: 100, + $soldAmount: 0, plans: [CustomPlan], $introduction: '

很棒的商品

', $isLaunched: false, @@ -59,6 +60,7 @@ export const CustomCreateProductsObj = { 'theater', 'price', 'amount', + 'soldAmount', 'startAt', 'endAt', 'sellStartAt', @@ -108,6 +110,12 @@ export const CustomCreateProductsObj = { min: 0, description: '票券總量,不得低於最大人數方案。', }, + soldAmount: { + type: 'number', + example: CustomProduct.$soldAmount, + min: 0, + description: '已銷售數量', + }, plans: { type: 'array', items: { diff --git a/src/types/product.type.ts b/src/types/product.type.ts index 95787aa..c9fb788 100644 --- a/src/types/product.type.ts +++ b/src/types/product.type.ts @@ -1,3 +1,6 @@ +import { IProduct } from '../models/product'; +import { Request } from 'express'; + export enum ProductType { premier = 'premier', corporateBooking = 'corporateBooking', @@ -33,3 +36,9 @@ export type TPlan = { discount: number; // 方案折扣數 headCount: number; // 該方案包含幾張票 }; + +export type TCreateProductsReq = Request< + unknown, + unknown, + { products: [IProduct] } +>;