feat : update
This commit is contained in:
@@ -15,6 +15,7 @@ import {
|
||||
extraSectionRows,
|
||||
extraSections,
|
||||
guides,
|
||||
guidePosterBleeding,
|
||||
guideSections,
|
||||
media,
|
||||
outboxEvents,
|
||||
@@ -137,6 +138,13 @@ export async function updateGuideBasics(guideId: string, input: unknown) {
|
||||
.limit(1);
|
||||
if (!poster) throw new Error("ไม่พบภาพตัวละครที่อัปโหลดแล้ว");
|
||||
}
|
||||
if (data.posterBleeding !== undefined) {
|
||||
const bleedingMediaIds = [...new Set(data.posterBleeding.map((layer) => layer.mediaId))];
|
||||
const bleedingMedia = bleedingMediaIds.length
|
||||
? await tx.select({ id: media.id }).from(media).where(and(inArray(media.id, bleedingMediaIds), eq(media.status, "ready")))
|
||||
: [];
|
||||
if (bleedingMedia.length !== bleedingMediaIds.length) throw new Error("ไม่พบภาพ Bleeding ที่อัปโหลดแล้ว");
|
||||
}
|
||||
const updated = await advanceGuideVersion(tx, guideId, data.expectedVersion);
|
||||
await tx.update(guides).set({
|
||||
overview: data.overview,
|
||||
@@ -148,6 +156,25 @@ export async function updateGuideBasics(guideId: string, input: unknown) {
|
||||
...(data.posterTopColors !== undefined ? { posterTopColors: data.posterTopColors } : {}),
|
||||
...(data.posterBottomColors !== undefined ? { posterBottomColors: data.posterBottomColors } : {}),
|
||||
}).where(eq(guides.id, guideId));
|
||||
if (data.posterBleeding !== undefined) {
|
||||
const previous = await tx.select({ mediaId: guidePosterBleeding.mediaId }).from(guidePosterBleeding).where(eq(guidePosterBleeding.guideId, guideId));
|
||||
for (const layer of previous) {
|
||||
await tx.update(media).set({ currentReferenceCount: sql`greatest(${media.currentReferenceCount} - 1, 0)` }).where(eq(media.id, layer.mediaId));
|
||||
}
|
||||
await tx.delete(guidePosterBleeding).where(eq(guidePosterBleeding.guideId, guideId));
|
||||
if (data.posterBleeding.length) {
|
||||
await tx.insert(guidePosterBleeding).values(data.posterBleeding.map((layer, sortOrder) => ({
|
||||
guideId,
|
||||
mediaId: layer.mediaId,
|
||||
amount: Math.round(layer.amount),
|
||||
scale: Math.round(layer.scale),
|
||||
sortOrder,
|
||||
})));
|
||||
for (const layer of data.posterBleeding) {
|
||||
await tx.update(media).set({ currentReferenceCount: sql`${media.currentReferenceCount} + 1` }).where(eq(media.id, layer.mediaId));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (current.coverMediaId !== data.coverMediaId) {
|
||||
await tx.update(media).set({ currentReferenceCount: sql`greatest(${media.currentReferenceCount} - 1, 0)` }).where(eq(media.id, current.coverMediaId));
|
||||
await tx.update(media).set({ currentReferenceCount: sql`${media.currentReferenceCount} + 1` }).where(eq(media.id, data.coverMediaId));
|
||||
@@ -412,11 +439,15 @@ export async function permanentlyDeleteGuide(guideId: string) {
|
||||
return getDb().transaction(async (tx) => {
|
||||
const [guide] = await tx.select().from(guides).where(eq(guides.id, guideId)).limit(1);
|
||||
if (!guide?.trashedAt) throw new Error("Guide ต้องอยู่ในถังขยะก่อนลบถาวร");
|
||||
const bleeding = await tx.select({ mediaId: guidePosterBleeding.mediaId }).from(guidePosterBleeding).where(eq(guidePosterBleeding.guideId, guideId));
|
||||
await tx.delete(guides).where(eq(guides.id, guideId));
|
||||
await tx.update(media).set({ currentReferenceCount: sql`greatest(${media.currentReferenceCount} - 1, 0)` }).where(eq(media.id, guide.coverMediaId));
|
||||
if (guide.posterArtMediaId) {
|
||||
await tx.update(media).set({ currentReferenceCount: sql`greatest(${media.currentReferenceCount} - 1, 0)` }).where(eq(media.id, guide.posterArtMediaId));
|
||||
}
|
||||
for (const layer of bleeding) {
|
||||
await tx.update(media).set({ currentReferenceCount: sql`greatest(${media.currentReferenceCount} - 1, 0)` }).where(eq(media.id, layer.mediaId));
|
||||
}
|
||||
await emitGuideEvents(tx, guide.id, guide.version + 1);
|
||||
return { version: guide.version + 1 };
|
||||
});
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
constellationRecommendations,
|
||||
extraSectionRows,
|
||||
guides,
|
||||
guidePosterBleeding,
|
||||
guideSections,
|
||||
glossaryAliases,
|
||||
media,
|
||||
@@ -124,13 +125,14 @@ export async function getGuideByCharacterKey(characterKey: string) {
|
||||
const materialKeys = [...new Set(
|
||||
row.character.ascension.flatMap((group) => group.items.map((item) => item.key)),
|
||||
)];
|
||||
const [materials, poster] = await Promise.all([
|
||||
const [materials, poster, bleeding] = await Promise.all([
|
||||
materialKeys.length
|
||||
? getDb().select().from(catalogMaterials).where(inArray(catalogMaterials.key, materialKeys))
|
||||
: [],
|
||||
row.guide.posterArtMediaId
|
||||
? getDb().select().from(media).where(eq(media.id, row.guide.posterArtMediaId)).limit(1)
|
||||
: Promise.resolve([]),
|
||||
getDb().select({ layer: guidePosterBleeding, asset: media }).from(guidePosterBleeding).leftJoin(media, eq(media.id, guidePosterBleeding.mediaId)).where(eq(guidePosterBleeding.guideId, row.guide.id)).orderBy(asc(guidePosterBleeding.sortOrder)),
|
||||
]);
|
||||
const materialByKey = new Map(materials.map((item) => [item.key, item]));
|
||||
const sections = await getDb().select().from(guideSections).where(eq(guideSections.guideId, row.guide.id)).orderBy(asc(guideSections.sortOrder));
|
||||
@@ -138,6 +140,7 @@ export async function getGuideByCharacterKey(characterKey: string) {
|
||||
...row.guide,
|
||||
coverUrl: publicMediaUrl(row.cover.objectKey),
|
||||
posterArtUrl: poster[0] ? publicMediaUrl(poster[0].objectKey) : null,
|
||||
posterBleeding: bleeding.map(({ layer, asset }) => ({ ...layer, imageUrl: asset ? publicMediaUrl(asset.objectKey) : null })),
|
||||
character: catalogCharacterWithUrls(row.character, materialByKey),
|
||||
sections,
|
||||
};
|
||||
@@ -159,7 +162,7 @@ async function getGuideContent(slug: string, publicOnly: boolean) {
|
||||
const materialKeys = [...new Set(
|
||||
row.character.ascension.flatMap((group) => group.items.map((item) => item.key)),
|
||||
)];
|
||||
const [sections, weaponConditionsRows, groups, profile, constellationGroups, teams, aliases, materials, poster] = await Promise.all([
|
||||
const [sections, weaponConditionsRows, groups, profile, constellationGroups, teams, aliases, materials, poster, bleeding] = await Promise.all([
|
||||
getDb().select().from(guideSections).where(and(eq(guideSections.guideId, row.guide.id), eq(guideSections.enabled, true))).orderBy(asc(guideSections.sortOrder)),
|
||||
getDb().select().from(weaponConditions).where(eq(weaponConditions.guideId, row.guide.id)).orderBy(asc(weaponConditions.sortOrder)),
|
||||
getDb().select().from(artifactGroups).where(eq(artifactGroups.guideId, row.guide.id)).orderBy(asc(artifactGroups.sortOrder)),
|
||||
@@ -173,6 +176,7 @@ async function getGuideContent(slug: string, publicOnly: boolean) {
|
||||
row.guide.posterArtMediaId
|
||||
? getDb().select().from(media).where(eq(media.id, row.guide.posterArtMediaId)).limit(1)
|
||||
: Promise.resolve([]),
|
||||
getDb().select({ layer: guidePosterBleeding, asset: media }).from(guidePosterBleeding).leftJoin(media, eq(media.id, guidePosterBleeding.mediaId)).where(eq(guidePosterBleeding.guideId, row.guide.id)).orderBy(asc(guidePosterBleeding.sortOrder)),
|
||||
]);
|
||||
const groupedAliases = aliasesByItem(aliases);
|
||||
const materialByKey = new Map(materials.map((item) => [item.key, item]));
|
||||
@@ -221,6 +225,7 @@ async function getGuideContent(slug: string, publicOnly: boolean) {
|
||||
...row.guide,
|
||||
coverUrl: publicMediaUrl(row.cover.objectKey),
|
||||
posterArtUrl: poster[0] ? publicMediaUrl(poster[0].objectKey) : null,
|
||||
posterBleeding: bleeding.map(({ layer, asset }) => ({ ...layer, imageUrl: asset ? publicMediaUrl(asset.objectKey) : null })),
|
||||
character: { ...catalogCharacterWithUrls(row.character, materialByKey), aliases: groupedAliases.character.get(row.character.key) ?? [] },
|
||||
elementIconUrls,
|
||||
sections,
|
||||
|
||||
@@ -41,6 +41,11 @@ export const updateGuideSchema = z.strictObject({
|
||||
posterArtOffsetY: z.number().finite().min(-50).max(50).optional(),
|
||||
posterTopColors: z.tuple([z.string().regex(/^#[0-9a-fA-F]{6}$/u), z.string().regex(/^#[0-9a-fA-F]{6}$/u)]).optional(),
|
||||
posterBottomColors: z.tuple([z.string().regex(/^#[0-9a-fA-F]{6}$/u), z.string().regex(/^#[0-9a-fA-F]{6}$/u)]).optional(),
|
||||
posterBleeding: z.array(z.strictObject({
|
||||
mediaId: z.string().uuid(),
|
||||
amount: z.number().finite().min(0).max(100),
|
||||
scale: z.number().finite().min(25).max(300),
|
||||
})).max(30).optional(),
|
||||
});
|
||||
|
||||
export const weaponSectionSchema = z.strictObject({
|
||||
|
||||
Reference in New Issue
Block a user