diff --git a/src/dto/group/groupFilterDto.ts b/src/dto/group/groupFilterDto.ts index 182e862..ca6ecca 100644 --- a/src/dto/group/groupFilterDto.ts +++ b/src/dto/group/groupFilterDto.ts @@ -6,6 +6,15 @@ import { } from '../../types/group.type'; import { SortOrder } from '../../types/common.type'; +type TDateRange = { + $and: { + time: { + $gte?: Date; + $lte?: Date; + }; + }[]; +}; + export class GroupFilterDto { private readonly _title?: string; private readonly _movieTitle?: string[]; @@ -13,11 +22,10 @@ export class GroupFilterDto { private readonly _participantCount?: number; private readonly _status: GroupStatus; private readonly _haveTicket?: boolean; - private readonly _startAt?: Date; - private readonly _endAt?: Date; private readonly _page: number; private readonly _limit: number; private readonly _sort: Record; + private readonly _dateRanges: TDateRange[] = []; get filter() { const titleRegex = this._title ? new RegExp(this._title) : undefined; @@ -30,12 +38,7 @@ export class GroupFilterDto { amount: { $eq: this._participantCount }, }), ...(this._haveTicket && { haveTicket: this._haveTicket }), - ...((this._startAt || this._endAt) && { - time: { - ...(this._endAt && { $lte: this._endAt }), - ...(this._startAt && { $gte: this._startAt }), - }, - }), + $or: this._dateRanges, }; } @@ -58,6 +61,10 @@ export class GroupFilterDto { }; } + get dateRanges() { + return this._dateRanges; + } + constructor(req: IGetGroupsReq) { const { title, @@ -66,8 +73,10 @@ export class GroupFilterDto { participantCount, status, haveTicket, - startAt, - endAt, + endTime, + startTime, + startDate, + endDate, page, limit, sortField, @@ -87,7 +96,43 @@ export class GroupFilterDto { this._theater = theater ? theater.split(',') : undefined; this._haveTicket = haveTicket === undefined ? undefined : haveTicket === 'true'; - this._startAt = startAt ? moment(startAt).toDate() : undefined; - this._endAt = endAt ? moment(endAt).toDate() : undefined; + + // date range + + const dateFrom = moment(startDate, 'YYYY/MM/DD').startOf('day'); + const dateTo = moment(endDate, 'YYYY/MM/DD').endOf('day'); + const timeFrom = moment(startTime, 'HH:mm'); + const timeTo = moment(endTime, 'HH:mm'); + + const dateRanges: TDateRange[] = []; + for ( + let date = dateFrom.clone(); + date.isSameOrBefore(dateTo); + date.add(1, 'days') + ) { + const start = { + hour: timeFrom.hours(), + minute: timeFrom.minutes(), + second: 0, + millisecond: 0, + }; + const end = { + hour: timeTo.hours(), + minute: timeTo.minutes(), + second: 0, + millisecond: 0, + }; + + const dateRange = { + $and: [ + { time: { $gte: new Date(date.clone().set(start).toISOString()) } }, + { time: { $lte: new Date(date.clone().set(end).toISOString()) } }, + ], + }; + + dateRanges.push(dateRange); + } + + this._dateRanges = dateRanges; } } diff --git a/src/routes/groupRoute.ts b/src/routes/groupRoute.ts index 34bd018..415030a 100644 --- a/src/routes/groupRoute.ts +++ b/src/routes/groupRoute.ts @@ -258,23 +258,33 @@ export class GroupRoute extends BaseRoute { $ref: "#/definitions/CustomGetGroupCountQuery" } } - #swagger.parameters['startAt'] = { + #swagger.parameters['startDate'] = { in: 'query', - required: false, - description: '開始活動時間-起', + required: true, + description: '開始活動日期-起 (一定要遵循以下格式)', type: 'string', - schema:{ - $ref: "#/definitions/CustomTimeAtFromQuery" - } + example: '2024/01/12' } - #swagger.parameters['endAt'] = { + #swagger.parameters['endDate'] = { in: 'query', - required: false, - description: '開始活動時間-迄', + required: true, + description: '開始活動日期-迄 (一定要遵循以下格式)', type: 'string', - schema:{ - $ref: "#/definitions/CustomTimeAtToQuery" - } + example:'2024/07/12' + } + #swagger.parameters['startTime'] = { + in: 'query', + required: true, + description: '開始活動時間-起 (一定要遵循以下格式)', + type: 'string', + example: '01:00' + } + #swagger.parameters['endTime'] = { + in: 'query', + required: true, + description: '開始活動時間-迄 (一定要遵循以下格式)', + type: 'string', + example: '23:00' } #swagger.parameters['sortField'] = { in: 'query', diff --git a/src/service/groupService.ts b/src/service/groupService.ts index e81901b..9c238f2 100644 --- a/src/service/groupService.ts +++ b/src/service/groupService.ts @@ -145,6 +145,15 @@ export class GroupService { } } public async findGroups(groupFilterDto: GroupFilterDto) { + const { dateRanges } = groupFilterDto; + + if (dateRanges.length === 0) { + throwError( + CustomResponseType.INVALID_GROUP_FILTER_MESSAGE + '時間區間有誤', + CustomResponseType.INVALID_GROUP_FILTER, + ); + } + return await this.groupRepository.findGroups(groupFilterDto); } diff --git a/src/types/group.type.ts b/src/types/group.type.ts index d442e0e..555e931 100644 --- a/src/types/group.type.ts +++ b/src/types/group.type.ts @@ -46,8 +46,10 @@ export interface IGetGroupsReq extends IUserReq { participantCount?: string; status?: GroupStatus; haveTicket?: string; - startAt?: string; - endAt?: string; + startTime?: string; + endTime?: string; + startDate?: string; + endDate?: string; }; } diff --git a/src/validator/group/getGroup.pipe.ts b/src/validator/group/getGroup.pipe.ts index 2a48925..8e96860 100644 --- a/src/validator/group/getGroup.pipe.ts +++ b/src/validator/group/getGroup.pipe.ts @@ -1,26 +1,12 @@ import { PipeBase } from '../pipe.base'; import { query } from 'express-validator'; import { CustomResponseType } from '../../types/customResponseType'; -import { - GroupSortField, - GroupStatus, - IGetGroupsReq, -} from '../../types/group.type'; -import { OptionType, TCustomValidator } from '../index.type'; +import { GroupSortField, GroupStatus } from '../../types/group.type'; +import { OptionType } from '../index.type'; import { SortOrder } from '../../types/common.type'; import { booleanStrings, nullableOption } from '../../utils/constants'; export class GetGroupsPipe extends PipeBase { - private validateStartAt: TCustomValidator = (value, { req }) => { - const { endAt } = (req as IGetGroupsReq).query; - return this.validatePeriod(value, endAt, (a, b) => a.isBefore(b)); - }; - - private validateEndAt: TCustomValidator = (value, { req }) => { - const { startAt } = (req as IGetGroupsReq).query; - return this.validatePeriod(value, startAt, (a, b) => a.isAfter(b)); - }; - public transform = () => [ this.limitValidation( query('limit'), @@ -46,20 +32,22 @@ export class GetGroupsPipe extends PipeBase { .withMessage( CustomResponseType.INVALID_GROUP_FILTER_MESSAGE + 'participantCount', ), - query('startAt') - .optional(nullableOption) - .custom(this.validateDate) - .custom(this.validateStartAt) + query('startTime') + .exists() .withMessage( - CustomResponseType.INVALID_GROUP_FILTER_MESSAGE + 'startAtTo', + CustomResponseType.INVALID_GROUP_FILTER_MESSAGE + 'startTime', ), - query('endAt') - .optional(nullableOption) - .custom(this.validateDate) - .custom(this.validateEndAt) + query('endTime') + .exists() + .withMessage(CustomResponseType.INVALID_GROUP_FILTER_MESSAGE + 'endTime'), + query('startDate') + .exists() .withMessage( - CustomResponseType.INVALID_GROUP_FILTER_MESSAGE + 'startAtTo', + CustomResponseType.INVALID_GROUP_FILTER_MESSAGE + 'startDate', ), + query('endDate') + .exists() + .withMessage(CustomResponseType.INVALID_GROUP_FILTER_MESSAGE + 'endDate'), query('sortField') .optional(nullableOption) .custom(this.validateOption(OptionType.item, GroupSortField)) diff --git a/src/validator/pipe.base.ts b/src/validator/pipe.base.ts index 13102d0..a2d2629 100644 --- a/src/validator/pipe.base.ts +++ b/src/validator/pipe.base.ts @@ -149,7 +149,10 @@ export abstract class PipeBase { this.validateDate(fromValue); return isNeedCheckPeriod - ? compareFn(moment(value), moment(fromValue)) + ? compareFn( + moment(value, moment.ISO_8601), + moment(fromValue, moment.ISO_8601), + ) : true; };