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, @InjectRepository(WordImage) private readonly wordImageRepository: Repository, @InjectRepository(Sentence) private readonly sentenceRepository: Repository, @InjectRepository(WordSentence) private readonly wordSentenceRepository: Repository, ) {} /** * 创建汉字 */ async create(createWordDto: CreateWordDto): Promise { const word = this.wordRepository.create(createWordDto); return await this.wordRepository.save(word); } /** * 查询汉字(支持多种查询条件和分页) */ async findAllPaginated(queryDto: QueryWordsDto): Promise { const where: FindOptionsWhere = {}; // 只查询汉字 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 { 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 { 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 { const word = await this.findOneById(id); await this.wordRepository.remove(word); } // 图片相关方法 /** * 获取汉字图片列表 */ async getWordImages(wordId: number): Promise { return await this.wordImageRepository.find({ where: { wordId: wordId }, order: { sortOrder: 'ASC' }, }); } /** * 更新图片排序 */ async updateWordImagesSort(wordId: number, imageIds: number[]): Promise { 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 { 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 { 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 { // 创建句子 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 { 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 { // TODO: 实现词语关联查询 return []; } }