src.DRG.Profit(盈亏分析 020102xx)、src.DRG.Warning(020101xx)、src.DRG.QC(020103xx)、src.DRG.System(020104xx)、src.DRG.SpecialAppeal(020105xx)、src.DRG.Workbench / src.DRG.Interface(020100xx)。没有独立的报表服务类,frontend/src/pages/Report/PatientFeeDetail.tsx 实际调用的是盈亏分析的 02010209。BS_DRGMedInsuMedicalRecord(MR):医保结算明细,含 27 项费用汇总列 + MedfeeSumamt、DRG/DRGDesc、HISAdmID、MedcasNo、MdtrtId、PsnNo、AdmDate/DscgDate、ImageURL、FixmedinsCode、ValiFlag(日期口径仅入院/出院,不使用结算日期);BS_HISPatAdm(PA):住院就诊,AdmID、PatName、PatMedicalNo、InsuType(险种类型存于本表)、InLocCode/InLocDesc、AdmDocCode/AdmDocDesc、AdmDateTime/DisDateTime;BS_HISFeeDetail(FD):HIS 费用明细流水,MedinsListCodg/MedinsListDesc、EcCode/EcDesc、Pric、Cnt、DetItemFeeSumamt、FeeOcurTime、Deleted、ValidFlag;BS_HISDiseInfo:诊断,AdmID(就诊关联,已确认)、DiagCode/DiagName/MainFlag/DiagSn;另有 Adm_Dr(指向 BS_HISPatAdm.ID 的对象引用,本报表不使用);无 Deleted 字段;BS_HISOprnInfo:手术,AdmID(就诊关联,已确认)、OprnCode/OprnName/MainFlag/OprnSn;同样含 Adm_Dr(不使用);无 Deleted 字段;BS_DRGGroupRecord(GR):分组记录,DRG/DRGDesc/BenchmarkCost/Weight/GroupStage/ActiveFlag;CB_DRGDictTable:险种字典表(已有)。父记录 Parent_Dr IS NULL AND Code='INSU_TYPE' AND Status='Y',子记录 Parent_Dr = 父.ID、Code=险种编码、Name=中文名。WITH(CTE)(报 SQLCODE -51),不支持 ORDER BY 聚合表达式与列序号(报 SQLCODE -73);可用:COUNT(DISTINCT col)、COALESCE、EXISTS 子查询、LEFT JOIN、GROUP BY、ORDER BY <普通列>;sortIdx(amount, idx) + $ORDER(...,-1))完成;Not ProcedureBlock,方法内需 new 隔离局部变量;$G() / %Get("xxx") 防御写法,统一 set params = jsonObj.params.%Get(0) 后直接属性访问;.cls 文件的最后一个成员不要是方法(IDE 自动重写会吞掉结束大括号),末尾追加 Parameter;CB_MapInterface 路由注册:ClassName 直接写业务类(如 src.DRG.Profit),ServiceType='S'、SessionFlag='Y'、TokenFlag='N'。医保办 / 运营管理(看差异、定控费策略)、信息科(部署与菜单授权)、开发(前后端实现)。
Goals:
src.DRG.Report 报表服务类(与 Profit 同级、号段 020106xx),作为后续所有报表的承载骨架;Non-Goals:
src.DRG.Profit 与 020102xx 现有接口;src.DRG.Report,号段 020106xx选择:新建 src/src/DRG/Report.cls(Class src.DRG.Report Extends (%RegisteredObject, %XML.Adaptor) [ Not ProcedureBlock ]),接口号段 020106xx。
理由:020100/020101/020102/020103/020104/020105 已分别归属分组、预警、盈亏、质控、系统、特例单议,020106 未被占用;报表与盈亏分析是不同关注点(盈亏=聚合统计,报表=横向比对/明细呈现),混在一个类会让 Profit.cls(已 110KB+)继续膨胀。
备选:继续挂 020102xx → 拒绝,号段语义混乱且加剧单文件膨胀。
选择:02010601 QueryComparableVisits(分页检索)+ 02010602 CompareDRGFee(按 admIds 一次性返回全部比对结果,不分页)。
理由:检索需要分页(结果集可能上千),比对最多 5 例、数据量可控且必须一次性拿全才能算并集与差异;拆开后前端可先浏览筛选再加入比对篮,交互与负载都更合理。
备选:单接口既检索又比对 → 拒绝,入参耦合、无法分页。
选择:MR 存在记录且 MR.ImageURL <> ''(首页已入库)且 EXISTS (SELECT 1 FROM BS_DRGGroupRecord g WHERE g.AdmID = MR.HISAdmID AND g.ActiveFlag='1' AND g.DRG<>'')(已有效分组)且 (MR.ValiFlag IS NULL OR MR.ValiFlag='1')。
理由:与 src.DRG.Workbench 中「病案分组」筛选口径(首页存在 + 事后分组记录存在)保持一致,且 ImageURL<>'' 是项目内既有的「首页已上传」判定方式。
EXISTS 子查询选择:mainDiagCode / mainDiagName / mainOprnCode / mainOprnName 通过 EXISTS (SELECT 1 FROM BS_HISDiseInfo d WHERE ... AND d.MainFlag=1) 与 EXISTS (... BS_HISOprnInfo o ... AND o.MainFlag='Y') 实现。
理由:本环境 IRIS 不支持 CTE 与派生表 JOIN;EXISTS 不影响主表行数、不会造成明细放大。
备选:LEFT JOIN 诊断/手术表后过滤 → 拒绝,会在主诊断多条时放大行数(LEFT JOIN + WHERE 还会退化成内连接)。
选择:CompareDRGFee 在后端依次执行 4 段查询(MR 汇总列 / FD 按项目聚合 / FD 按类别聚合 / 诊断手术支付标准),在 ObjectScript 侧用下标数组构建行并集、计算差异矩阵,并对 feeItem / feeCategory 按金额降序排序(不用 SQL ORDER BY 聚合)。
理由:SQL 侧无法表达「跨 N 例的全外连接并集」,UNION + 多例组合会产生 N 倍 SQL;并集在内存构建最直观。排序受 SQLCODE -73 限制必须放应用层。
first / avg,后端算、前端只着色选择:入参 diffBase(first 缺省 / avg);后端为每个单元格计算 diffFlag / diffAmount / diffRate,前端不做任何比较计算,仅按 diffFlag 着色。
理由:差异口径变更只需改后端一次;前端不重复实现业务规则。数值相等判定采用 2 位小数、容差 0.01,避免浮点误差造成「假差异」。
选择:后端 %BuildReportText 用 ObjectScript 拼装,输出 analysis.reportText: string[];前端逐段渲染 + 「复制文本」按钮。
理由:用户明确选定后端生成,保证报告口径与差异矩阵同源;文案规则变更集中在后端一处。
备选:前端拼装 → 拒绝,会造成前后端口径漂移。
选择:待比对列表 rowSelection(type: 'checkbox'),最多勾选 5 条;已选以「比对篮」Tag 形式展示、可移除;比对结果区用 Tabs 分「结算清单费用汇总 / HIS费用明细项目 / 结算类别聚合」三 Tab,上方固定诊断/手术/支付标准信息卡,下方分析报告卡。
理由:用户选定无固定基准的并列对比;Tab 分区避免三类粒度混在一张表造成阅读灾难。
选择:「导出Excel」将当前 Tab 的比对矩阵(行 + 各例列 + 差异列)转成带 BOM 的 CSV,MIME application/vnd.ms-excel;charset=utf-8,.xls 后缀;优先 window.showSaveFilePicker,非安全上下文(内网 http)回落 a.download。
理由:与 Workbench.tsx、PatientFeeDetail.tsx 完全一致,零第三方依赖;内网 http 下必然走回落,属预期行为。
withHosp选择:新建 frontend/src/api/report.ts,导出 withHospReport(沿用 profit.ts 的 withHosp 语义);页面用 useHospitalOptions() 取机构选项与默认值(非管理员锁定当前登录机构)。
理由:与 Profit 各页面保持一致。
选择:新增私有辅助 %GetInsuTypeName(code),用 CB_DRGDictTable 自关联一次性查出险种中文名,后端在 02010601 列表与 02010602 比对结果中直接输出中文 insuType(同时保留编码 insuTypeCode);查不到时回落为原编码值。险种过滤条件作用于 PA.InsuType(险种存在 BS_HISPatAdm,与 Profit.cls 的 %BuildWhere 一致)。
已验证可用的现成写法(Workbench.cls 第 216 行 / HisDataService.cls 第 1606 行):
SELECT b.Name INTO :insuTypeName
FROM CB_DRGDictTable a INNER JOIN CB_DRGDictTable b ON a.ID = b.Parent_Dr
WHERE a.Parent_Dr IS NULL AND a.Code='INSU_TYPE' AND a.Status='Y' AND b.Code=:insuType
理由:用户明确要求映射放后端;字典表已存在且项目内多处已用同一 SQL,口径统一。
备选:前端常量映射 → 拒绝,字典变更需发版前端。
补充:因险种过滤需 PA.InsuType,02010601 主 SQL 必须 INNER JOIN BS_HISPatAdm PA ON PA.AdmID = MR.HISAdmID AND PA.Deleted=0(不能只 LEFT JOIN 后过滤,会退化且语义不符)。
feeItem 截断阈值 Top 500选择:feeItem 并集行数超过 500 行时,按各例金额最大值降序截断到 500 行,并在响应 analysis 与 reportText 中追加提示「费用明细项目较多,仅展示金额 Top 500 项」。
理由:用户确认阈值;避免 5 例 × 数千条明细导致的接口超时与前端渲染卡顿。截断基于聚合后的并集行数,不基于单例明细条数。
本次无 DDL 变更,仅读取以下已有表字段:
| 表(别名) | 用途 | 关键字段 |
|---|---|---|
BS_DRGMedInsuMedicalRecord (MR) |
检索主表、结算清单费用汇总列、支付信息 | HISAdmID、MedcasNo、PsnName、Gend、Age、AdmDate、DscgDate、IptDays、DRG、DRGDesc、MedfeeSumamt、MdtrtId、PsnNo、FixmedinsCode、ImageURL、ValiFlag + 27 项费用列(无结算日期字段,日期口径仅 AdmDate/DscgDate) |
BS_HISPatAdm (PA) |
科室/医生/住院日期/险种类型 | AdmID、PatName、PatMedicalNo、InsuType、InLocCode、InLocDesc、AdmDocCode、AdmDocDesc、AdmDateTime、DisDateTime、Deleted |
BS_HISFeeDetail (FD) |
费用明细(Tab2/Tab3) | AdmID、PatMedicalNo、MedinsListCodg、MedinsListDesc、EcCode、EcDesc、Pric、Cnt、DetItemFeeSumamt、FeeOcurTime、Deleted、ValidFlag |
BS_HISDiseInfo |
诊断比对 | AdmID(就诊号,直接关联 MR.HISAdmID)、DiagCode、DiagName、MainFlag、DiagSn(无 Deleted 字段) |
BS_HISOprnInfo |
手术比对 | AdmID(就诊号,直接关联 MR.HISAdmID)、OprnCode、OprnName、MainFlag、OprnSn(无 Deleted 字段) |
BS_DRGGroupRecord (GR) |
分组与支付标准 | AdmID、MedcasNo、DRG、DRGDesc、BenchmarkCost、Weight、GroupStage、ActiveFlag |
CB_DRGDictTable |
险种编码 → 中文名 | 父:ID、Code='INSU_TYPE'、Parent_Dr IS NULL、Status='Y';子:Parent_Dr、Code(险种编码)、Name(中文名) |
关联键:
MR.HISAdmID = PA.AdmIDFD.AdmID = MR.HISAdmIDGR.AdmID = MR.HISAdmIDBS_HISDiseInfo.AdmID = MR.HISAdmID(已确认,Adm_Dr 为指向 BS_HISPatAdm.ID 的对象引用,本报表不依赖、不做反查)BS_HISOprnInfo.AdmID = MR.HISAdmID(已确认,同上)有效记录口径:
(ValiFlag IS NULL OR ValiFlag='1');Deleted=0;Deleted=0 AND (ValidFlag IS NULL OR ValidFlag<>'N');ActiveFlag='1';Deleted 字段,不做作废过滤;主诊断用 MainFlag=1、主手术用 MainFlag='Y' 判主,其余全部纳入并集。已确认结论(用户 2026-09-17 确认,无需再查库):
BS_HISDiseInfo/BS_HISOprnInfo的就诊关联字段为AdmID,直接与MR.HISAdmID关联;Adm_Dr是指向BS_HISPatAdm表ID的对象引用,本报表不使用(禁止Adm_Dr -> BS_HISPatAdm.ID反查写法);- 取消结算日期类型,
dateType仅支持adm(MR.AdmDate)/discharge(MR.DscgDate),缺省adm;- 险种类型有字典表
CB_DRGDictTable,中文映射 放后端;feeItem截断阈值定为 Top 500 行;BS_HISDiseInfo/BS_HISOprnInfo均无Deleted字段,不做作废过滤。
ClassName='src.DRG.Report'、MethodName='QueryComparableVisits'、ServiceType='S'、SessionFlag='Y'、TokenFlag='N'。请求:
{
"code": "02010601",
"params": [{
"fixmedinsCode": "H51010001",
"dateType": "discharge",
"startDate": "2026-01-01",
"endDate": "2026-03-31",
"drgCode": "CB55",
"patName": "", "medcasNo": "", "admID": "",
"mdtrtId": "", "psnNo": "", "insuType": "",
"mainDiagCode": "", "mainDiagName": "",
"mainOprnCode": "", "mainOprnName": ""
}],
"pagination": [{ "pageSize": 20, "currentPage": 1 }]
}
响应:
{
"errorCode": "0",
"errorMessage": "",
"result": {
"rows": [{
"admID": "A001", "medcasNo": "M001", "patName": "张三", "gend": "男", "age": 62,
"deptCode": "D01", "deptName": "眼科", "doctorCode": "DOC1", "doctorName": "李医生",
"admDate": "2026-01-05", "dscgDate": "2026-01-15", "iptDays": 10,
"drgCode": "CB55", "drgName": "晶状体手术",
"medfeeSumamt": 25000, "payStandard": 20000,
"insuType": "职工基本医疗保险", "insuTypeCode": "310",
"psnNo": "5101***", "mdtrtId": "MD001",
"fixmedinsCode": "H51010001"
}],
"total": 128
}
}
dateType 取值:仅 adm(入院日期,MR.AdmDate)/ discharge(出院日期,MR.DscgDate),缺省 adm;已取消结算日期类型,传入其它值一律按 adm 处理。
insuType(高级区入参)= 险种编码,过滤 PA.InsuType;insuType(响应)= 险种中文名(后端查 CB_DRGDictTable 映射,见 D11),并同时返回编码 insuTypeCode;映射不到时中文名回落为编码原值。
ClassName='src.DRG.Report'、MethodName='CompareDRGFee'、ServiceType='S'、SessionFlag='Y'、TokenFlag='N';不使用分页。请求:
{
"code": "02010602",
"params": [{ "admIds": ["A001", "A002", "A003"], "diffBase": "first" }]
}
响应(结构摘要):
{
"errorCode": "0",
"errorMessage": "",
"result": {
"visits": [
{ "admID": "A001", "medcasNo": "M001", "patName": "张三", "deptName": "眼科",
"doctorName": "李医生", "admDate": "2026-01-05", "dscgDate": "2026-01-15",
"iptDays": 10, "insuType": "职工医保" }
],
"drgPayment": [
{ "admID": "A001", "drgCode": "CB55", "drgName": "晶状体手术",
"payStandard": 20000, "weight": 1.82, "totalFee": 25000,
"profitAmount": -5000, "profitRate": -20 }
],
"settleSummary": [
{ "field": "WmFee", "fieldName": "西药费",
"cells": [
{ "admID": "A001", "value": 3200.5, "diffFlag": "same", "diffAmount": 0, "diffRate": 0 },
{ "admID": "A002", "value": 5100.0, "diffFlag": "diff", "diffAmount": 1899.5, "diffRate": 59.35 }
],
"maxAmount": 5100, "minAmount": 3200.5, "rangeAmount": 1899.5 }
],
"feeItem": [
{ "itemCode": "1001", "itemName": "人工晶体", "spec": "", "unit": "片",
"cells": [
{ "admID": "A001", "value": 8000, "qty": 1, "diffFlag": "same", "diffAmount": 0, "diffRate": 0 },
{ "admID": "A002", "value": 0, "qty": 0, "diffFlag": "missing", "diffAmount": -8000, "diffRate": -100 }
],
"maxAmount": 8000, "minAmount": 0, "rangeAmount": 8000 }
],
"feeCategory": [
{ "categoryCode": "01", "categoryName": "西药费",
"cells": [ { "admID": "A001", "value": 3200.5, "diffFlag": "same", "diffAmount": 0, "diffRate": 0 } ],
"maxAmount": 3200.5, "minAmount": 3200.5, "rangeAmount": 0 }
],
"diagnoses": [
{ "diagCode": "H25.900", "diagName": "老年性白内障",
"marks": [ { "admID": "A001", "exists": true, "isMain": true } ] }
],
"operations": [
{ "oprnCode": "13.4100", "oprnName": "白内障超声乳化抽吸术",
"marks": [ { "admID": "A001", "exists": true, "isMain": true } ] }
],
"analysis": {
"reportText": [
"【结论摘要】本次共比对 3 例就诊……",
"【费用总览】……",
"【结构差异 Top5】……",
"【诊断与手术差异】……",
"【支付标准与盈亏】……",
"【风险提示】……"
]
}
}
}
字段约定:
diffFlag 取值:same / diff / missing / onlyIn;diffBase='first' 时首例各单元格 diffAmount=0 / diffFlag='same';diffBase='avg' 时以各例均值为基准;$FN(x,"",2) 保留 2 位;diffRate 保留 2 位;useHospitalOptions() 拉机构列表并默认选中当前登录机构;02010601 → 渲染待比对列表(CustomPagination 分页,金额列合计行);02010602;visits)+ DRG 支付标准卡(drgPayment);Tabs 三维度比对表,差异单元格按 diffFlag 着色(diff 橙色/红色、onlyIn 蓝色、missing 灰色);reportText 逐段渲染 + 复制按钮);Radio.Group(与首条对比 / 与平均值对比)→ 重新调 02010602(前端不重算);MedinsListCodg 聚合再比对;单次最多 5 例;feeItem 行数超阈值(如 500 行)时按金额 Top N 截断并在报告与页面上提示。ORDER BY 聚合、WITH(CTE) 在本环境不可用。→ 排序下沉到 ObjectScript;主诊断/主手术用 EXISTS;不使用派生表 JOIN。BS_HISDiseInfo / BS_HISOprnInfo 就诊关联字段存在三种写法。→ 实施第一步先在生产库确认实际列名,再写代码(任务清单已列为准入门槛)。size="small" + 横向滚动 + Tab 懒渲染(仅渲染当前 Tab)。InsuType、结算类别 EcCode 等字典值需中文展示。→ 优先取表中已有描述列(EcDesc);InsuType 中文映射若在后端无字典表,则后端原值透传,前端用既有常量映射,无映射时显示原值。.cls 末尾为方法时结束大括号可能被吞。→ 类末尾追加 Parameter 成员;写入 20 秒后复查并统计 {/} 数量。src/src/DRG/Report.cls 新建类并编译:Do $SYSTEM.OBJ.Compile("src.DRG.Report","ck");db/register_interface_02010601.sql、db/register_interface_02010602.sql,SELECT 校验各返回 1 行;npm run build + tsc --noEmit 通过后部署 dist 到生产(E:\DRG\dist,nginx 8082);db/insert_fee_comparison_menu.sql,为相应角色分配菜单权限,登录后验证页面可访问;回滚:DELETE FROM CB_MapInterface WHERE Code IN ('02010601','02010602'); + 删除菜单与角色授权记录 + 回退前端 dist 包;Report.cls 可保留(未被调用即无影响)。
(无。全部已确认。)
已关闭:
诊断/手术关联字段是→ 确认为AdmID还是Adm_DrAdmID结算日期类型→ 确认取消,仅入院/出院险种是否有字典表、映射放哪端→ 有CB_DRGDictTable,映射放后端→ 定为 Top 500feeItem截断阈值诊断/手术是否有→ 均无,不做作废过滤Deleted字段