From 3c6175d95952c6f0e2d55e7e0064cbc1cd297fdd Mon Sep 17 00:00:00 2001
From: nenohi <kimutipartylove@gmail.com>
Date: Sat, 8 Jul 2023 08:56:11 +0900
Subject: [PATCH] =?UTF-8?q?=E5=BA=83=E5=91=8A=E3=81=AE=E6=9B=9C=E6=97=A5?=
 =?UTF-8?q?=E3=82=92=E8=A8=AD=E5=AE=9A=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88?=
 =?UTF-8?q?=E3=81=86=E3=81=AB=20(#10095)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* 曜日選択できるように

* ラベル選択でもチェックが変更されるように

* adを参照しないといけないかも

* smallint -> integer

* 異物混入だったので取りだし

* タイムゾーン指定(Date2つ使うのなんか違和感

* 未テスト

* これにすると出てこないかも

* UIチョット変更

* UI変更 fix bug

* 畳むように修正

* dayofweek->dayOfWeek

* マイグレ時にnot null,default設定してるのでnullable:falseでよさそう

* コメントの記載

* Update packages/backend/src/server/api/endpoints/meta.ts

Co-authored-by: Acid Chicken (硫酸鶏) <root@acid-chicken.com>

---------

Co-authored-by: Acid Chicken (硫酸鶏) <root@acid-chicken.com>
---
 locales/ja-JP.yml                             |  1 +
 .../backend/migration/1677054292210-ad4.js    |  9 +++++++++
 packages/backend/src/models/entities/Ad.ts    |  5 ++++-
 .../server/api/endpoints/admin/ad/create.ts   |  4 +++-
 .../server/api/endpoints/admin/ad/update.ts   |  4 +++-
 .../backend/src/server/api/endpoints/meta.ts  | 20 +++++++++++--------
 packages/frontend/src/pages/admin/ads.vue     | 19 ++++++++++++++++++
 7 files changed, 51 insertions(+), 11 deletions(-)
 create mode 100644 packages/backend/migration/1677054292210-ad4.js

diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 0b2817e1ad..51122655b5 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1451,6 +1451,7 @@ _ad:
   back: "戻る"
   reduceFrequencyOfThisAd: "この広告の表示頻度を下げる"
   hide: "表示しない"
+  timezoneinfo: "曜日はサーバーのタイムゾーンを元に指定されます。"
 
 _forgotPassword:
   enterEmail: "アカウントに登録したメールアドレスを入力してください。そのアドレス宛てに、パスワードリセット用のリンクが送信されます。"
diff --git a/packages/backend/migration/1677054292210-ad4.js b/packages/backend/migration/1677054292210-ad4.js
new file mode 100644
index 0000000000..48499319b4
--- /dev/null
+++ b/packages/backend/migration/1677054292210-ad4.js
@@ -0,0 +1,9 @@
+export class ad1677054292210 {
+	name = 'ad1677054292210';
+	async up(queryRunner) {
+			await queryRunner.query(`ALTER TABLE "ad" ADD "dayOfWeek" integer NOT NULL Default 0`);
+	}
+	async down(queryRunner) {
+		await queryRunner.query(`ALTER TABLE "ad" DROP COLUMN "dayOfWeek"`);
+	}
+}
diff --git a/packages/backend/src/models/entities/Ad.ts b/packages/backend/src/models/entities/Ad.ts
index 56baf863ca..a496a6d276 100644
--- a/packages/backend/src/models/entities/Ad.ts
+++ b/packages/backend/src/models/entities/Ad.ts
@@ -55,7 +55,10 @@ export class Ad {
 		length: 8192, nullable: false,
 	})
 	public memo: string;
-
+	@Column('integer', {
+		default: 0, nullable: false,
+	})
+	public dayOfWeek: number;
 	constructor(data: Partial<Ad>) {
 		if (data == null) return;
 
diff --git a/packages/backend/src/server/api/endpoints/admin/ad/create.ts b/packages/backend/src/server/api/endpoints/admin/ad/create.ts
index 917242db3f..757030839e 100644
--- a/packages/backend/src/server/api/endpoints/admin/ad/create.ts
+++ b/packages/backend/src/server/api/endpoints/admin/ad/create.ts
@@ -22,8 +22,9 @@ export const paramDef = {
 		expiresAt: { type: 'integer' },
 		startsAt: { type: 'integer' },
 		imageUrl: { type: 'string', minLength: 1 },
+		dayOfWeek: { type: 'integer' },
 	},
-	required: ['url', 'memo', 'place', 'priority', 'ratio', 'expiresAt', 'startsAt', 'imageUrl'],
+	required: ['url', 'memo', 'place', 'priority', 'ratio', 'expiresAt', 'startsAt', 'imageUrl', 'dayOfWeek'],
 } as const;
 
 // eslint-disable-next-line import/no-default-export
@@ -41,6 +42,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 				createdAt: new Date(),
 				expiresAt: new Date(ps.expiresAt),
 				startsAt: new Date(ps.startsAt),
+				dayOfWeek: ps.dayOfWeek,
 				url: ps.url,
 				imageUrl: ps.imageUrl,
 				priority: ps.priority,
diff --git a/packages/backend/src/server/api/endpoints/admin/ad/update.ts b/packages/backend/src/server/api/endpoints/admin/ad/update.ts
index dbab7e9d4f..70082290ba 100644
--- a/packages/backend/src/server/api/endpoints/admin/ad/update.ts
+++ b/packages/backend/src/server/api/endpoints/admin/ad/update.ts
@@ -31,8 +31,9 @@ export const paramDef = {
 		ratio: { type: 'integer' },
 		expiresAt: { type: 'integer' },
 		startsAt: { type: 'integer' },
+		dayOfWeek: { type: 'integer' },
 	},
-	required: ['id', 'memo', 'url', 'imageUrl', 'place', 'priority', 'ratio', 'expiresAt', 'startsAt'],
+	required: ['id', 'memo', 'url', 'imageUrl', 'place', 'priority', 'ratio', 'expiresAt', 'startsAt', 'dayOfWeek'],
 } as const;
 
 // eslint-disable-next-line import/no-default-export
@@ -56,6 +57,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 				imageUrl: ps.imageUrl,
 				expiresAt: new Date(ps.expiresAt),
 				startsAt: new Date(ps.startsAt),
+				dayOfWeek: ps.dayOfWeek,
 			});
 		});
 	}
diff --git a/packages/backend/src/server/api/endpoints/meta.ts b/packages/backend/src/server/api/endpoints/meta.ts
index 6ef5f0d5c8..915a1e54f8 100644
--- a/packages/backend/src/server/api/endpoints/meta.ts
+++ b/packages/backend/src/server/api/endpoints/meta.ts
@@ -1,4 +1,4 @@
-import { IsNull, LessThanOrEqual, MoreThan } from 'typeorm';
+import { IsNull, LessThanOrEqual, MoreThan, Brackets } from 'typeorm';
 import { Inject, Injectable } from '@nestjs/common';
 import JSON5 from 'json5';
 import type { AdsRepository, UsersRepository } from '@/models/index.js';
@@ -263,13 +263,16 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 		super(meta, paramDef, async (ps, me) => {
 			const instance = await this.metaService.fetch(true);
 
-			const ads = await this.adsRepository.find({
-				where: {
-					expiresAt: MoreThan(new Date()),
-					startsAt: LessThanOrEqual(new Date()),
-				},
-			});
-
+			const ads = await this.adsRepository.createQueryBuilder("ads")
+				.where('ads.expiresAt > :now', { now: new Date() })
+				.andWhere('ads.startsAt <= :now', { now: new Date() })
+				.andWhere(new Brackets(qb => {
+					// 曜日のビットフラグを確認する
+					qb.where('ads.dayOfWeek & :dayOfWeek > 0', { dayOfWeek: 1 << new Date().getDay() })
+						.orWhere('ads.dayOfWeek = 0');
+				}))
+				.getMany();
+		
 			const response: any = {
 				maintainerName: instance.maintainerName,
 				maintainerEmail: instance.maintainerEmail,
@@ -311,6 +314,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 					place: ad.place,
 					ratio: ad.ratio,
 					imageUrl: ad.imageUrl,
+					dayOfWeek: ad.dayOfWeek,
 				})),
 				enableEmail: instance.enableEmail,
 				enableServiceWorker: instance.enableServiceWorker,
diff --git a/packages/frontend/src/pages/admin/ads.vue b/packages/frontend/src/pages/admin/ads.vue
index 2c9e18b0bf..9a5bd88b2e 100644
--- a/packages/frontend/src/pages/admin/ads.vue
+++ b/packages/frontend/src/pages/admin/ads.vue
@@ -36,6 +36,16 @@
 						<template #label>{{ i18n.ts.expiration }}</template>
 					</MkInput>
 				</FormSplit>
+				<MkFolder>
+					<template #label>{{ i18n.ts.advancedSettings }}</template>
+					<span>
+						{{ i18n.ts._ad.timezoneinfo }}
+						<div v-for="(day, index) in daysOfWeek" :key="index">
+							<input :id="`ad${ad.id}-${index}`" type="checkbox" :checked="(ad.dayOfWeek & (1 << index)) !== 0" @change="toggleDayOfWeek(ad, index)">
+							<label :for="`ad${ad.id}-${index}`">{{ day }}</label>
+						</div>
+					</span>
+				</MkFolder>
 				<MkTextarea v-model="ad.memo">
 					<template #label>{{ i18n.ts.memo }}</template>
 				</MkTextarea>
@@ -59,6 +69,7 @@ import MkButton from '@/components/MkButton.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkTextarea from '@/components/MkTextarea.vue';
 import MkRadios from '@/components/MkRadios.vue';
+import MkFolder from '@/components/MkFolder.vue';
 import FormSplit from '@/components/form/split.vue';
 import * as os from '@/os';
 import { i18n } from '@/i18n';
@@ -69,6 +80,7 @@ let ads: any[] = $ref([]);
 // ISO形式はTZがUTCになってしまうので、TZ分ずらして時間を初期化
 const localTime = new Date();
 const localTimeDiff = localTime.getTimezoneOffset() * 60 * 1000;
+const daysOfWeek: string[] = [i18n.ts._weekday.sunday, i18n.ts._weekday.monday, i18n.ts._weekday.tuesday, i18n.ts._weekday.wednesday, i18n.ts._weekday.thursday, i18n.ts._weekday.friday, i18n.ts._weekday.saturday];
 
 os.api('admin/ad/list').then(adsResponse => {
 	ads = adsResponse.map(r => {
@@ -84,6 +96,11 @@ os.api('admin/ad/list').then(adsResponse => {
 	});
 });
 
+// 選択された曜日(index)のビットフラグを操作する
+function toggleDayOfWeek(ad, index) {
+	ad.dayOfWeek ^= 1 << index;
+}
+
 function add() {
 	ads.unshift({
 		id: null,
@@ -95,6 +112,7 @@ function add() {
 		imageUrl: null,
 		expiresAt: null,
 		startsAt: null,
+		dayOfWeek: 0,
 	});
 }
 
@@ -105,6 +123,7 @@ function remove(ad) {
 	}).then(({ canceled }) => {
 		if (canceled) return;
 		ads = ads.filter(x => x !== ad);
+		if (ad.id == null) return;
 		os.apiWithDialog('admin/ad/delete', {
 			id: ad.id,
 		});
-- 
GitLab