重构
This commit is contained in:
30
src/modules/dictionary/dictionary-action-dto.ts
Normal file
30
src/modules/dictionary/dictionary-action-dto.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { ValidateError } from "@/lib/errors";
|
||||
import { TSharedItem } from "@/shared";
|
||||
import z from "zod";
|
||||
|
||||
const DictionaryActionInputDtoSchema = z.object({
|
||||
text: z.string().min(1, 'Empty text.').max(30, 'Text too long.'),
|
||||
queryLang: z.string().min(1, 'Query lang too short.').max(20, 'Query lang too long.'),
|
||||
forceRelook: z.boolean(),
|
||||
definitionLang: z.string().min(1, 'Definition lang too short.').max(20, 'Definition lang too long.'),
|
||||
userId: z.string().optional()
|
||||
});
|
||||
|
||||
export type DictionaryActionInputDto = z.infer<typeof DictionaryActionInputDtoSchema>;
|
||||
|
||||
export const validateDictionaryActionInput = (dto: DictionaryActionInputDto): DictionaryActionInputDto => {
|
||||
const result = DictionaryActionInputDtoSchema.safeParse(dto);
|
||||
if (result.success) return result.data;
|
||||
|
||||
const errorMessages = result.error.issues.map((issue) =>
|
||||
`${issue.path.join('.')}: ${issue.message}`
|
||||
).join('; ');
|
||||
|
||||
throw new ValidateError(`Validation failed: ${errorMessages}`);
|
||||
};
|
||||
|
||||
export type DictionaryActionOutputDto = {
|
||||
message: string,
|
||||
success: boolean;
|
||||
data?: TSharedItem;
|
||||
};
|
||||
27
src/modules/dictionary/dictionary-action.ts
Normal file
27
src/modules/dictionary/dictionary-action.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
"use server";
|
||||
|
||||
import { DictionaryActionInputDto, DictionaryActionOutputDto, validateDictionaryActionInput } from "./dictionary-action-dto";
|
||||
import { ValidateError } from "@/lib/errors";
|
||||
import { lookUpService } from "./dictionary-service";
|
||||
|
||||
export const lookUpDictionaryAction = async (dto: DictionaryActionInputDto): Promise<DictionaryActionOutputDto> => {
|
||||
try {
|
||||
return {
|
||||
message: 'success',
|
||||
success: true,
|
||||
data: await lookUpService(validateDictionaryActionInput(dto))
|
||||
};
|
||||
} catch (e) {
|
||||
if (e instanceof ValidateError) {
|
||||
return {
|
||||
success: false,
|
||||
message: e.message
|
||||
};
|
||||
}
|
||||
console.log(e);
|
||||
return {
|
||||
success: false,
|
||||
message: 'Unknown error occured.'
|
||||
};
|
||||
}
|
||||
};
|
||||
38
src/modules/dictionary/dictionary-repository-dto.ts
Normal file
38
src/modules/dictionary/dictionary-repository-dto.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { TSharedItem } from "@/shared";
|
||||
|
||||
export type CreateDictionaryLookUpInputDto = {
|
||||
userId?: string;
|
||||
text: string;
|
||||
queryLang: string;
|
||||
definitionLang: string;
|
||||
dictionaryItemId?: number;
|
||||
};
|
||||
|
||||
export type SelectLastLookUpResultOutputDto = TSharedItem & {id: number} | null;
|
||||
|
||||
export type CreateDictionaryItemInputDto = {
|
||||
standardForm: string;
|
||||
queryLang: string;
|
||||
definitionLang: string;
|
||||
};
|
||||
|
||||
export type CreateDictionaryEntryInputDto = {
|
||||
itemId: number;
|
||||
ipa?: string;
|
||||
definition: string;
|
||||
partOfSpeech?: string;
|
||||
example: string;
|
||||
};
|
||||
|
||||
export type CreateDictionaryEntryWithoutItemIdInputDto = {
|
||||
ipa?: string;
|
||||
definition: string;
|
||||
partOfSpeech?: string;
|
||||
example: string;
|
||||
};
|
||||
|
||||
export type SelectLastLookUpResultInputDto = {
|
||||
text: string,
|
||||
queryLang: string,
|
||||
definitionLang: string;
|
||||
};
|
||||
86
src/modules/dictionary/dictionary-repository.ts
Normal file
86
src/modules/dictionary/dictionary-repository.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import { stringNormalize } from "@/utils/string";
|
||||
import {
|
||||
CreateDictionaryEntryInputDto,
|
||||
CreateDictionaryEntryWithoutItemIdInputDto,
|
||||
CreateDictionaryItemInputDto,
|
||||
CreateDictionaryLookUpInputDto,
|
||||
SelectLastLookUpResultInputDto,
|
||||
SelectLastLookUpResultOutputDto,
|
||||
} from "./dictionary-repository-dto";
|
||||
import prisma from "@/lib/db";
|
||||
|
||||
export async function selectLastLookUpResult(dto: SelectLastLookUpResultInputDto): Promise<SelectLastLookUpResultOutputDto> {
|
||||
const result = await prisma.dictionaryLookUp.findFirst({
|
||||
where: {
|
||||
normalizedText: stringNormalize(dto.text),
|
||||
queryLang: dto.queryLang,
|
||||
definitionLang: dto.definitionLang,
|
||||
dictionaryItemId: {
|
||||
not: null
|
||||
}
|
||||
},
|
||||
include: {
|
||||
dictionaryItem: {
|
||||
include: {
|
||||
entries: true
|
||||
}
|
||||
}
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: 'desc'
|
||||
}
|
||||
});
|
||||
if (result && result.dictionaryItem) {
|
||||
const item = result.dictionaryItem;
|
||||
return {
|
||||
id: item.id,
|
||||
standardForm: item.standardForm,
|
||||
entries: item.entries.filter(v => !!v).map(v => {
|
||||
return {
|
||||
ipa: v.ipa || undefined,
|
||||
definition: v.definition,
|
||||
partOfSpeech: v.partOfSpeech || undefined,
|
||||
example: v.example
|
||||
};
|
||||
})
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export async function createLookUp(content: CreateDictionaryLookUpInputDto) {
|
||||
return (await prisma.dictionaryLookUp.create({
|
||||
data: { ...content, normalizedText: stringNormalize(content.text) }
|
||||
})).id;
|
||||
}
|
||||
|
||||
export async function createLookUpWithItemAndEntries(
|
||||
itemData: CreateDictionaryItemInputDto,
|
||||
lookUpData: CreateDictionaryLookUpInputDto,
|
||||
entries: CreateDictionaryEntryWithoutItemIdInputDto[]
|
||||
) {
|
||||
return await prisma.$transaction(async (tx) => {
|
||||
const item = await tx.dictionaryItem.create({
|
||||
data: itemData
|
||||
});
|
||||
|
||||
await tx.dictionaryLookUp.create({
|
||||
data: {
|
||||
...lookUpData,
|
||||
normalizedText: stringNormalize(lookUpData.text),
|
||||
dictionaryItemId: item.id
|
||||
}
|
||||
});
|
||||
|
||||
for (const entry of entries) {
|
||||
await tx.dictionaryEntry.create({
|
||||
data: {
|
||||
...entry,
|
||||
itemId: item.id
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return item.id;
|
||||
});
|
||||
}
|
||||
11
src/modules/dictionary/dictionary-service-dto.ts
Normal file
11
src/modules/dictionary/dictionary-service-dto.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { TSharedItem } from "@/shared";
|
||||
|
||||
export type LookUpServiceInputDto = {
|
||||
text: string,
|
||||
queryLang: string,
|
||||
definitionLang: string,
|
||||
forceRelook: boolean,
|
||||
userId?: string;
|
||||
};
|
||||
|
||||
export type LookUpServiceOutputDto = TSharedItem;
|
||||
61
src/modules/dictionary/dictionary-service.ts
Normal file
61
src/modules/dictionary/dictionary-service.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import { executeDictionaryLookup } from "@/lib/bigmodel/dictionary";
|
||||
import { createLookUp, createLookUpWithItemAndEntries, selectLastLookUpResult } from "./dictionary-repository";
|
||||
import { LookUpServiceInputDto } from "./dictionary-service-dto";
|
||||
|
||||
export const lookUpService = async (dto: LookUpServiceInputDto) => {
|
||||
const {
|
||||
text,
|
||||
queryLang,
|
||||
userId,
|
||||
definitionLang,
|
||||
forceRelook
|
||||
} = dto;
|
||||
|
||||
const lastLookUpResult = await selectLastLookUpResult({
|
||||
text,
|
||||
queryLang,
|
||||
definitionLang,
|
||||
});
|
||||
|
||||
if (forceRelook || !lastLookUpResult) {
|
||||
const response = await executeDictionaryLookup(
|
||||
text,
|
||||
queryLang,
|
||||
definitionLang
|
||||
);
|
||||
|
||||
// 使用事务确保数据一致性
|
||||
createLookUpWithItemAndEntries(
|
||||
{
|
||||
standardForm: response.standardForm,
|
||||
queryLang,
|
||||
definitionLang
|
||||
},
|
||||
{
|
||||
userId,
|
||||
text,
|
||||
queryLang,
|
||||
definitionLang,
|
||||
},
|
||||
response.entries
|
||||
).catch(error => {
|
||||
console.error('Failed to save dictionary data:', error);
|
||||
});
|
||||
|
||||
return response;
|
||||
} else {
|
||||
createLookUp({
|
||||
userId: userId,
|
||||
text: text,
|
||||
queryLang: queryLang,
|
||||
definitionLang: definitionLang,
|
||||
dictionaryItemId: lastLookUpResult.id
|
||||
}).catch(error => {
|
||||
console.error('Failed to save dictionary data:', error);
|
||||
});
|
||||
return {
|
||||
standardForm: lastLookUpResult.standardForm,
|
||||
entries: lastLookUpResult.entries
|
||||
};
|
||||
}
|
||||
};
|
||||
2
src/modules/dictionary/index.ts
Normal file
2
src/modules/dictionary/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from "./dictionary-action";
|
||||
export * from "./dictionary-action-dto";
|
||||
Reference in New Issue
Block a user