chore: src/modules/knowledge/service/film.ts

This commit is contained in:
2026-07-01 17:44:59 +08:00
parent bb3d2a2fbc
commit 6fc21281ab
+127
View File
@@ -0,0 +1,127 @@
import { KnowledgeFilmEntity } from './../entity/film';
import { Inject, Provide } from '@midwayjs/core';
import { BaseService } from '@cool-midway/core';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { Repository } from 'typeorm';
import { escapeObject } from '../utils/xss';
/**
* 知识库模块-电影信息
*/
@Provide()
export class KnowledgeFilmService extends BaseService {
@InjectEntityModel(KnowledgeFilmEntity)
knowledgeFilmEntity: Repository<KnowledgeFilmEntity>;
@Inject()
ctx;
/**
* 新增电影
* - 业务层查重:检查同名电影是否已存在
* - 对用户输入进行 XSS 过滤,防止存储型 XSS 攻击
* - 排除 posters 字段(JSON数组,不需要转义)
* @param params 电影数据
*/
async add(params: any) {
// 业务层查重:检查同名电影是否已存在
if (params.name) {
const exist = await this.knowledgeFilmEntity.findOne({
where: { name: params.name }
});
if (exist) {
throw new Error(`电影"${params.name}"已存在,请勿重复添加`);
}
}
// XSS 过滤 - 排除 posters 字段(JSON 数组)
const safeParams = escapeObject(params, ['posters']);
return super.add(safeParams);
}
/**
* 更新电影
* - 对用户输入进行 XSS 过滤,防止存储型 XSS 攻击
* - 排除 posters 字段(JSON数组,不需要转义)
* @param params 电影数据
*/
async update(params: any) {
// XSS 过滤 - 排除 posters 字段(JSON 数组)
const safeParams = escapeObject(params, ['posters']);
return super.update(safeParams);
}
/**
* API: importFilms - 批量导入电影数据
* - 业务层查重:检查同名电影是否已存在,已存在则跳过
* - 将 Excel 导入的数据逐条插入数据库
* - 跳过名称为空的记录
* - 对文本字段进行 XSS 过滤
* @param films 电影数据数组(来自 Excel 解析)
* @returns 导入结果统计
*/
async importFilms(films: any[]) {
const successList: string[] = [];
const failList: { name: string; error: string }[] = [];
const skipList: string[] = [];
for (const film of films) {
// 跳过名称为空的记录
if (!film.name) {
failList.push({ name: film.name || '(空)', error: '名称不能为空' });
continue;
}
// 业务层查重:检查同名电影是否已存在
const exist = await this.knowledgeFilmEntity.findOne({
where: { name: film.name }
});
if (exist) {
skipList.push(film.name);
continue;
}
try {
// 映射 Excel 列名到数据库字段
const filmData = {
name: film.name || '',
director: film.director || film['导演'] || film['导演/作者'] || '',
year: film.year || film['年份'] || null,
country: film.country || film['国家'] || '',
language: film.language || film['语言'] || '',
mainCharacters: film.mainCharacters || film['主要人物'] || '',
synopsis: film.synopsis || film['内容简介'] || '',
backgroundStory: film.backgroundStory || film['背景故事'] || '',
posters: film.posters || film['海报链接']
? [film.posters || film['海报链接']]
: [],
categoryId: film.categoryId ?? film['分类ID'] ?? film['分类'] ?? 45,
quality: film.quality || film['质量'] || film['质量评级'] || 'C',
watched: film.watched || film['是否已看'] || false,
doubanRating: film.doubanRating || film['豆瓣评分'] || null,
tmdbRating: film.tmdbRating || film['TMDB评分'] || null,
link: film.link || film['链接'] || '',
honors: film.honors || film['荣誉'] || '',
highlights: film.highlights || film['亮点所在'] || '',
whyWorthWatching: film.whyWorthWatching || film['为什么值得一看'] || ''
};
// XSS 过滤
const safeData = escapeObject(filmData, ['posters']);
await this.knowledgeFilmEntity.insert(safeData);
successList.push(filmData.name);
} catch (e: any) {
failList.push({ name: film.name, error: e.message || '导入失败' });
}
}
return {
total: films.length,
success: successList.length,
fail: failList.length,
skip: skipList.length,
successList,
failList,
skipList
};
}
}