Files
AIclinicalresearch/backend/src/modules/dc/tool-b/services/TemplateService.ts
HaHafeng 19f9c5ea93 docs(deployment): Fix 8 critical deployment issues and enhance documentation
Summary of fixes:
- Fix service discovery address (change .sae domain to internal IP)
- Unify timezone configuration (Asia/Shanghai for all services)
- Enhance ECS security group configuration (Redis/Weaviate port binding)
- Add image pull strategy best practices
- Add Python service memory management guidelines
- Update Dify API Key deployment strategy (avoid deadlock)
- Add SSH tunnel for RDS database access
- Add NAT gateway cost optimization explanation

Modified files (7 docs):
- 00-部署架构总览.md (enhanced with 7 sections)
- 03-Dify-ECS部署完全指南.md (security hardening)
- 04-Python微服务-SAE容器部署指南.md (timezone + service discovery)
- 05-Node.js后端-SAE容器部署指南.md (timezone configuration)
- PostgreSQL部署策略-摸底报告.md (timezone best practice)
- 07-关键配置补充说明.md (3 new sections)
- 08-部署检查清单.md (service address fix)

New files:
- 文档修正报告-20251214.md (comprehensive fix report)
- Review documents from technical team

Impact:
- Fixed 3 P0/P1 critical issues (100% connection failure risk)
- Fixed 3 P2 important issues (stability and maintainability)
- Added 2 P3 best practices (developer convenience)

Status: All deployment documents reviewed and corrected, ready for production deployment
2025-12-14 13:25:28 +08:00

263 lines
7.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* DC模块 - 模板服务
*
* 功能:
* - 管理预设提取模板(疾病类型 + 报告类型)
* - 提供模板列表查询
* - Seed初始数据3个预设模板
*
* 平台能力复用:
* - ✅ prisma: 数据库操作
* - ✅ logger: 日志记录
*/
import { prisma } from '../../../../config/database.js';
import { logger } from '../../../../common/logging/index.js';
export interface TemplateField {
name: string;
desc: string;
width?: string; // TailwindCSS class
}
export interface Template {
id: string;
diseaseType: string;
reportType: string;
displayName: string;
fields: TemplateField[];
promptTemplate: string;
}
export class TemplateService {
/**
* 获取所有模板
*/
async getAllTemplates(): Promise<Template[]> {
try {
logger.info('[Template] Fetching all templates');
const templates = await prisma.dCTemplate.findMany({
orderBy: [{ diseaseType: 'asc' }, { reportType: 'asc' }]
});
logger.info('[Template] Templates fetched', { count: templates.length });
return templates.map(t => ({
id: t.id,
diseaseType: t.diseaseType,
reportType: t.reportType,
displayName: t.displayName,
fields: t.fields as TemplateField[],
promptTemplate: t.promptTemplate
}));
} catch (error) {
logger.error('[Template] Failed to fetch templates', { error });
throw error;
}
}
/**
* 根据疾病和报告类型获取模板
*/
async getTemplate(diseaseType: string, reportType: string): Promise<Template | null> {
try {
logger.info('[Template] Fetching template', { diseaseType, reportType });
const template = await prisma.dCTemplate.findUnique({
where: {
diseaseType_reportType: { diseaseType, reportType }
}
});
if (!template) {
logger.warn('[Template] Template not found', { diseaseType, reportType });
return null;
}
return {
id: template.id,
diseaseType: template.diseaseType,
reportType: template.reportType,
displayName: template.displayName,
fields: template.fields as TemplateField[],
promptTemplate: template.promptTemplate
};
} catch (error) {
logger.error('[Template] Failed to fetch template', { error, diseaseType, reportType });
throw error;
}
}
/**
* 初始化预设模板Seed数据
*
* 包括3个预设模板
* 1. 肺癌病理报告
* 2. 糖尿病入院记录
* 3. 高血压门诊病历
*/
async seedTemplates(): Promise<void> {
try {
logger.info('[Template] Seeding templates');
const templates = [
// 1. 肺癌病理报告
{
diseaseType: 'lung_cancer',
reportType: 'pathology',
displayName: '肺癌病理报告',
fields: [
{ name: '病理类型', desc: '如:浸润性腺癌、鳞状细胞癌', width: 'w-40' },
{ name: '分化程度', desc: '高/中/低分化', width: 'w-32' },
{ name: '肿瘤大小', desc: '最大径单位cm', width: 'w-32' },
{ name: '淋巴结转移', desc: '有/无及具体组别', width: 'w-48' },
{ name: '免疫组化', desc: '关键指标', width: 'w-56' }
],
promptTemplate: `你是一名病理学专家。请从以下肺癌病理报告中提取关键信息。
提取字段(必须返回以下所有字段):
- 病理类型:病理诊断类型(如浸润性腺癌、鳞状细胞癌)
- 分化程度:分化等级(高分化、中分化、低分化、未提及)
- 肿瘤大小肿瘤最大径单位cm
- 淋巴结转移:淋巴结转移情况(有/无及具体组别)
- 免疫组化:关键免疫组化指标
**输出格式严格的JSON格式不要有任何额外文本**
\`\`\`json
{
"病理类型": "...",
"分化程度": "...",
"肿瘤大小": "...",
"淋巴结转移": "...",
"免疫组化": "..."
}
\`\`\`
如果某个信息未在报告中提及,请填写"未提及"。`
},
// 2. 糖尿病入院记录
{
diseaseType: 'diabetes',
reportType: 'admission',
displayName: '糖尿病入院记录',
fields: [
{ name: '主诉', desc: '患者入院的主要症状', width: 'w-48' },
{ name: '现病史', desc: '发病过程', width: 'w-64' },
{ name: '既往史', desc: '糖尿病病史年限', width: 'w-40' },
{ name: '空腹血糖', desc: '单位mmol/L', width: 'w-32' },
{ name: '糖化血红蛋白', desc: '单位%', width: 'w-32' }
],
promptTemplate: `你是一名内分泌科专家。请从以下糖尿病患者入院记录中提取关键信息。
提取字段(必须返回以下所有字段):
- 主诉:患者入院时的主要症状
- 现病史:本次发病的过程和表现
- 既往史:糖尿病病史年限
- 空腹血糖最近的空腹血糖值单位mmol/L
- 糖化血红蛋白最近的HbA1c值单位%
**输出格式严格的JSON格式**
\`\`\`json
{
"主诉": "...",
"现病史": "...",
"既往史": "...",
"空腹血糖": "...",
"糖化血红蛋白": "..."
}
\`\`\`
如果某个信息未在记录中提及,请填写"未提及"。`
},
// 3. 高血压门诊病历
{
diseaseType: 'hypertension',
reportType: 'outpatient',
displayName: '高血压门诊病历',
fields: [
{ name: '血压值', desc: '单位mmHg', width: 'w-32' },
{ name: '心率', desc: '单位次/分', width: 'w-24' },
{ name: '当前用药', desc: '高血压药物', width: 'w-56' },
{ name: '靶器官损害', desc: '心/脑/肾', width: 'w-40' },
{ name: '危险分层', desc: '低/中/高/极高危', width: 'w-32' }
],
promptTemplate: `你是一名心内科专家。请从以下高血压患者门诊病历中提取关键信息。
提取字段(必须返回以下所有字段):
- 血压值:收缩压/舒张压单位mmHg
- 心率:心率(单位次/分)
- 当前用药:患者当前服用的高血压药物
- 靶器官损害:心脏、脑、肾脏等靶器官损害情况
- 危险分层:心血管风险分层(低危、中危、高危、极高危)
**输出格式严格的JSON格式**
\`\`\`json
{
"血压值": "...",
"心率": "...",
"当前用药": "...",
"靶器官损害": "...",
"危险分层": "..."
}
\`\`\`
如果某个信息未在病历中提及,请填写"未提及"。`
}
];
// 使用upsert避免重复
for (const template of templates) {
await prisma.dCTemplate.upsert({
where: {
diseaseType_reportType: {
diseaseType: template.diseaseType,
reportType: template.reportType
}
},
update: {
displayName: template.displayName,
fields: template.fields,
promptTemplate: template.promptTemplate
},
create: template
});
}
logger.info('[Template] Templates seeded successfully', { count: templates.length });
} catch (error) {
logger.error('[Template] Failed to seed templates', { error });
throw error;
}
}
}
// 导出单例
export const templateService = new TemplateService();