feat:初始化项目
This commit is contained in:
@@ -0,0 +1,291 @@
|
||||
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 [];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user