Files
platform-pipi/apps/api/src/modules/words/words.service.ts
T
2026-01-23 17:42:48 +08:00

292 lines
9.0 KiB
TypeScript

import { Injectable, NotFoundException, Logger } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, FindOptionsWhere, In } from 'typeorm';
import { Word, WordType, WordStatus } from './word.entity';
import { WordImage } from './word-image.entity';
import { Sentence } from './sentence.entity';
import { WordSentence } from './word-sentence.entity';
import { CreateWordDto } from './dto/create-word.dto';
import { UpdateWordDto } from './dto/update-word.dto';
import { QueryWordsDto } from './dto/query-words.dto';
import { CreateSentenceDto } from './dto/create-sentence.dto';
import { PaginatedWordData } from './dto/paginated-response.dto';
@Injectable()
export class WordsService {
private readonly logger = new Logger(WordsService.name);
constructor(
@InjectRepository(Word)
private readonly wordRepository: Repository<Word>,
@InjectRepository(WordImage)
private readonly wordImageRepository: Repository<WordImage>,
@InjectRepository(Sentence)
private readonly sentenceRepository: Repository<Sentence>,
@InjectRepository(WordSentence)
private readonly wordSentenceRepository: Repository<WordSentence>,
) {}
/**
* 创建汉字
*/
async create(createWordDto: CreateWordDto): Promise<Word> {
const word = this.wordRepository.create(createWordDto);
return await this.wordRepository.save(word);
}
/**
* 查询汉字(支持多种查询条件和分页)
*/
async findAllPaginated(queryDto: QueryWordsDto): Promise<PaginatedWordData> {
const where: FindOptionsWhere<Word> = {};
// 只查询汉字
where.type = WordType.CHINESE_CHAR;
// 搜索条件
if (queryDto.search) {
// 使用 Like 进行模糊搜索
const queryBuilder = this.wordRepository.createQueryBuilder('word');
queryBuilder.where('word.type = :type', { type: WordType.CHINESE_CHAR });
queryBuilder.andWhere('word.content LIKE :search', {
search: `%${queryDto.search}%`,
});
if (queryDto.grade) {
queryBuilder.andWhere('word.grade = :grade', { grade: queryDto.grade });
}
// 排序:按年级升序(低年级在前),年级相同时按创建时间
queryBuilder.orderBy('word.grade', 'ASC', 'NULLS LAST');
queryBuilder.addOrderBy('word.createdAt', 'ASC');
queryBuilder.addOrderBy('word.id', 'ASC');
// 分页参数
const page = queryDto.page || 1;
const limit = queryDto.limit || 9;
const skip = (page - 1) * limit;
// 加载关联数据
queryBuilder.leftJoinAndSelect('word.images', 'images');
queryBuilder.leftJoinAndSelect('word.wordSentences', 'wordSentences');
queryBuilder.leftJoinAndSelect('wordSentences.sentence', 'sentence');
// 分页
queryBuilder.skip(skip).take(limit);
const [list, total] = await queryBuilder.getManyAndCount();
// 计算总页数
const total_page = Math.ceil(total / limit);
return {
list,
pagination: {
total,
total_page,
page_size: limit,
current_page: page,
},
};
}
// 年级筛选
if (queryDto.grade) {
where.grade = queryDto.grade;
}
// 分页参数
const page = queryDto.page || 1;
const limit = queryDto.limit || 9;
const skip = (page - 1) * limit;
// 查询总数
const total = await this.wordRepository.count({ where });
// 查询分页数据
const list = await this.wordRepository.find({
where,
relations: ['images', 'wordSentences', 'wordSentences.sentence'],
order: {
grade: 'ASC',
createdAt: 'ASC',
id: 'ASC',
},
skip,
take: limit,
});
// 计算总页数
const total_page = Math.ceil(total / limit);
return {
list,
pagination: {
total,
total_page,
page_size: limit,
current_page: page,
},
};
}
/**
* 根据 ID 查询单个汉字
*/
async findOneById(id: number): Promise<Word> {
const word = await this.wordRepository.findOne({
where: { id },
relations: ['images', 'wordSentences', 'wordSentences.sentence'],
order: {
images: { sortOrder: 'ASC' },
wordSentences: { sortOrder: 'ASC' },
},
});
if (!word) {
throw new NotFoundException(`未找到ID为 ${id} 的汉字`);
}
return word;
}
/**
* 更新汉字信息
*/
async update(id: number, updateWordDto: UpdateWordDto): Promise<Word> {
const word = await this.findOneById(id);
// 更新基础信息
Object.assign(word, updateWordDto);
// 更新关联句子
if (updateWordDto.sentenceIds) {
// 删除现有关联
await this.wordSentenceRepository.delete({ wordId: id });
// 创建新关联
for (let i = 0; i < updateWordDto.sentenceIds.length; i++) {
const sentenceId = updateWordDto.sentenceIds[i];
const wordSentence = this.wordSentenceRepository.create({
wordId: id,
sentenceId: sentenceId,
sortOrder: i,
});
await this.wordSentenceRepository.save(wordSentence);
}
}
return await this.wordRepository.save(word);
}
/**
* 删除汉字
*/
async remove(id: number): Promise<void> {
const word = await this.findOneById(id);
await this.wordRepository.remove(word);
}
// 图片相关方法
/**
* 获取汉字图片列表
*/
async getWordImages(wordId: number): Promise<WordImage[]> {
return await this.wordImageRepository.find({
where: { wordId: wordId },
order: { sortOrder: 'ASC' },
});
}
/**
* 更新图片排序
*/
async updateWordImagesSort(wordId: number, imageIds: number[]): Promise<WordImage[]> {
for (let i = 0; i < imageIds.length; i++) {
await this.wordImageRepository.update(
{ id: imageIds[i], wordId: wordId },
{ sortOrder: i },
);
}
return await this.getWordImages(wordId);
}
/**
* 删除汉字图片
*/
async deleteWordImage(wordId: number, imageId: number): Promise<void> {
const image = await this.wordImageRepository.findOne({
where: { id: imageId, wordId: wordId },
});
if (!image) {
throw new NotFoundException('图片不存在');
}
await this.wordImageRepository.remove(image);
}
// 句子相关方法
/**
* 获取汉字句子列表
*/
async getWordSentences(wordId: number): Promise<Sentence[]> {
const wordSentences = await this.wordSentenceRepository.find({
where: { wordId: wordId },
relations: ['sentence'],
order: { sortOrder: 'ASC' },
});
return wordSentences.map((ws) => ws.sentence);
}
/**
* 添加句子到汉字
*/
async addSentenceToWord(wordId: number, createSentenceDto: CreateSentenceDto): Promise<Sentence> {
// 创建句子
const sentence = this.sentenceRepository.create(createSentenceDto);
const savedSentence = await this.sentenceRepository.save(sentence);
// 创建关联
const maxSort = await this.wordSentenceRepository
.createQueryBuilder('ws')
.where('ws.wordId = :wordId', { wordId })
.select('MAX(ws.sortOrder)', 'max')
.getRawOne();
const wordSentence = this.wordSentenceRepository.create({
wordId: wordId,
sentenceId: savedSentence.id,
sortOrder: (maxSort?.max || 0) + 1,
});
await this.wordSentenceRepository.save(wordSentence);
return savedSentence;
}
/**
* 从汉字中删除句子
*/
async removeSentenceFromWord(wordId: number, sentenceId: number): Promise<void> {
const wordSentence = await this.wordSentenceRepository.findOne({
where: { wordId: wordId, sentenceId: sentenceId },
});
if (!wordSentence) {
throw new NotFoundException('句子关联不存在');
}
await this.wordSentenceRepository.remove(wordSentence);
}
// 词语相关方法(后续实现)
/**
* 获取关联词语
*/
async getRelatedWords(wordId: number): Promise<Word[]> {
// TODO: 实现词语关联查询
return [];
}
}