江苏工伤联网接口数据库设计文档.md 55 KB

工伤联网接口数据库设计文档

版本信息

版本号 修改内容 修改日期 修改人
V1.0 初始版本设计 2024.12.19 系统架构师

目录


1 设计概述

1.1 业务需求

  1. 工伤病人流程:先调用工伤接口 → 存储调用记录 → 再调用HIS接口 → 存储HIS数据
  2. 非工伤病人流程:只调用HIS接口 → 存储HIS数据
  3. 接口类型支持:认证类、业务类、体检类、转院类、对账类、下载类
  4. 设计原则:简单、实用、可靠、通用、不为每个接口单独设计

1.2 设计思路

采用3表设计方案,通过通用化设计支持所有工伤接口类型:

  • 工伤接口调用记录表:记录所有工伤接口的调用情况
  • 工伤病人关联信息表:管理HIS病人与工伤系统的对应关系
  • 工伤业务数据记录表:存储具体的业务数据和结果

2 数据库表设计

2.1 工伤接口调用记录表 (WorkInjury_Interface_Log)

作用:记录每一次工伤接口的调用情况,包括请求参数、响应结果、调用状态等 应用场景:所有工伤接口调用都要记录,用于日志追踪、问题排查、数据分析

CREATE TABLE WorkInjury_Interface_Log (
    -- 主键信息
    LogId BIGINT IDENTITY(1,1) PRIMARY KEY,
    
    -- 业务关联信息(用于关联HIS系统的病人和就诊)
    HisPatientId VARCHAR(50),                    -- HIS系统病人ID(病人表主键)
    HisVisitNo VARCHAR(50),                      -- HIS系统就诊流水号/住院号
    WorkInjuryTransactionId VARCHAR(50),         -- 工伤系统交易流水号
    
    -- 接口调用信息(标识调用的是哪个工伤接口)
    InterfaceCode VARCHAR(10) NOT NULL,          -- 接口编号:9001签到,2201登记,2207结算等
    InterfaceName VARCHAR(100),                  -- 接口名称:签到,门诊登记,费用结算等
    InterfaceCategory VARCHAR(20),               -- 接口分类:认证类,业务类,体检类,对账类,下载类
    
    -- 调用参数记录(完整保存调用的入参和返回参数)
    RequestData NTEXT,                           -- 请求参数JSON:调用工伤接口时的完整入参
    ResponseData NTEXT,                          -- 响应参数JSON:工伤接口返回的完整数据
    
    -- 调用结果信息(判断接口调用是否成功)
    CallResult BIT NOT NULL DEFAULT 0,           -- 调用结果:0失败,1成功
    ResultCode VARCHAR(20),                      -- 结果编码:工伤接口返回的错误码
    ResultMessage NVARCHAR(500),                 -- 结果描述:错误信息或成功提示
    
    -- 工伤系统返回的关键信息(用于后续业务关联)
    WorkInjuryMsgId VARCHAR(50),                 -- 工伤系统报文ID:用于冲正等操作
    WorkInjurySignNo VARCHAR(50),                -- 工伤系统签到流水号:业务操作必需
    WorkInjuryQualificationId VARCHAR(50),       -- 工伤资格审核ID:登记时返回的资格ID
    
    -- 时间和操作信息(记录操作过程信息)
    CallTime DATETIME NOT NULL DEFAULT GETDATE(), -- 调用时间:接口实际调用的时间
    OperatorId VARCHAR(50),                      -- 操作员ID:谁操作的
    OperatorName NVARCHAR(50),                   -- 操作员姓名:操作员中文名
    TerminalId VARCHAR(50),                      -- 终端ID:哪台机器操作的
    
    -- 扩展字段(用于存储特殊数据)
    Remark NVARCHAR(500),                        -- 备注:额外说明信息
    ExtendData NTEXT,                            -- 扩展数据JSON:特殊业务数据
    
    -- 创建和修改信息
    CreateTime DATETIME NOT NULL DEFAULT GETDATE(),
    UpdateTime DATETIME NOT NULL DEFAULT GETDATE()
);

-- 创建索引(提高查询性能)
CREATE INDEX IX_WorkInjury_Interface_Log_Patient ON WorkInjury_Interface_Log(HisPatientId, CallTime);
CREATE INDEX IX_WorkInjury_Interface_Log_Visit ON WorkInjury_Interface_Log(HisVisitNo, InterfaceCode);
CREATE INDEX IX_WorkInjury_Interface_Log_Interface ON WorkInjury_Interface_Log(InterfaceCode, CallTime);
CREATE INDEX IX_WorkInjury_Interface_Log_MsgId ON WorkInjury_Interface_Log(WorkInjuryMsgId);

2.2 工伤病人关联信息表 (WorkInjury_Patient_Relation)

作用:管理HIS系统病人与工伤系统的对应关系,判断病人是否为工伤性质 应用场景:病人登记时判断是否需要调用工伤接口,读卡时建立关联关系

CREATE TABLE WorkInjury_Patient_Relation (
    -- 主键信息
    RelationId BIGINT IDENTITY(1,1) PRIMARY KEY,
    
    -- HIS系统病人信息(关联HIS病人表)
    HisPatientId VARCHAR(50) NOT NULL,           -- HIS系统病人ID:HIS病人表主键
    HisCardNo VARCHAR(50),                       -- HIS病人卡号:院内就诊卡号
    HisPatientName NVARCHAR(50),                 -- HIS病人姓名:在HIS中的姓名
    HisIdCard VARCHAR(30),                       -- HIS身份证号:HIS中的身份证
    
    -- 工伤系统病人信息(来自工伤读卡接口返回数据)
    WorkInjuryCardNo VARCHAR(50),                -- 工伤社保卡号:工伤系统的卡号
    WorkInjuryPsnNo VARCHAR(50),                 -- 工伤个人唯一识别码:工伤系统病人标识
    WorkInjuryPsnName NVARCHAR(50),              -- 工伤系统病人姓名:工伤系统中的姓名
    WorkInjuryIdCard VARCHAR(30),                -- 工伤系统身份证号:可能与HIS不完全一致
    WorkInjuryInsuranceArea VARCHAR(20),         -- 工伤保险统筹区:如"宿迁市"
    
    -- 工伤资格信息(判断病人工伤性质和资格)
    IsWorkInjuryPatient BIT NOT NULL DEFAULT 0,  -- 是否工伤病人:0否,1是
    WorkInjuryType VARCHAR(20),                  -- 工伤类型:门诊,住院,体检等
    QualificationId VARCHAR(50),                 -- 工伤医疗费资格审核信息ID
    QualificationStatus VARCHAR(20),             -- 资格审核状态:有效,过期,暂停等
    QualificationExpireDate DATETIME,            -- 资格有效期:过期需重新审核
    
    -- 最近读卡信息(记录最新的工伤读卡数据)
    LastReadCardTime DATETIME,                   -- 最后读卡时间:最近一次成功读卡时间
    LastReadCardData NTEXT,                      -- 最后读卡返回数据JSON:完整的读卡结果
    
    -- 状态信息
    Status CHAR(1) NOT NULL DEFAULT '1',         -- 状态:1有效,0无效,2暂停
    
    -- 扩展字段
    Remark NVARCHAR(500),                        -- 备注:特殊说明
    ExtendData NTEXT,                            -- 扩展数据JSON:其他相关数据
    
    -- 创建和修改信息
    CreateTime DATETIME NOT NULL DEFAULT GETDATE(),
    UpdateTime DATETIME NOT NULL DEFAULT GETDATE()
);

-- 创建索引和约束
CREATE UNIQUE INDEX IX_WorkInjury_Patient_Relation_HisPatient ON WorkInjury_Patient_Relation(HisPatientId);
CREATE INDEX IX_WorkInjury_Patient_Relation_WorkInjury ON WorkInjury_Patient_Relation(WorkInjuryPsnNo, WorkInjuryCardNo);
CREATE INDEX IX_WorkInjury_Patient_Relation_IdCard ON WorkInjury_Patient_Relation(WorkInjuryIdCard);

2.3 工伤业务数据记录表 (WorkInjury_Business_Data)

作用:存储具体的工伤业务数据,如登记信息、结算信息、处方明细、体检数据等 应用场景:保存业务办理的详细数据,用于业务查询、数据统计、单据打印等

CREATE TABLE WorkInjury_Business_Data (
    -- 主键信息
    BusinessId BIGINT IDENTITY(1,1) PRIMARY KEY,
    
    -- 关联信息(与前两个表关联)
    LogId BIGINT,                                -- 关联接口调用记录表ID:哪次接口调用产生的数据
    HisPatientId VARCHAR(50),                    -- HIS系统病人ID:对应病人表
    HisVisitNo VARCHAR(50),                      -- HIS系统就诊号:门诊号或住院号
    
    -- 业务类型信息(标识业务类型)
    BusinessType VARCHAR(20) NOT NULL,           -- 业务类型:登记,结算,处方,体检,对账,下载等
    BusinessCode VARCHAR(10),                    -- 业务编码:2201,2207,2204,8104,1320等
    BusinessStatus VARCHAR(20),                  -- 业务状态:成功,失败,撤销,已冲正等
    
    -- 工伤系统业务关键信息(来自工伤接口返回)
    WorkInjuryRegisterNo VARCHAR(50),            -- 工伤登记流水号:登记成功后返回
    WorkInjurySettleNo VARCHAR(50),              -- 工伤结算流水号:结算成功后返回
    WorkInjuryPreSettleId VARCHAR(50),           -- 工伤预结算ID:预结算返回的ID
    
    -- 费用信息(结算相关业务使用)
    TotalFee DECIMAL(15,2),                      -- 总费用:本次业务涉及的总金额
    WorkInjuryFee DECIMAL(15,2),                 -- 工伤基金支付:工伤保险支付金额
    PersonalFee DECIMAL(15,2),                   -- 个人支付:病人自付金额
    HospitalFee DECIMAL(15,2),                   -- 医院垫付:医院需要垫付的金额
    
    -- 业务详细数据(JSON格式存储复杂数据)
    BusinessDetailData NTEXT,                    -- 业务明细数据JSON:处方明细,费用明细,体检项目等
    HisBusinessData NTEXT,                       -- HIS系统业务数据JSON:HIS相关的业务数据
    WorkInjuryResponseData NTEXT,                -- 工伤接口完整返回JSON:完整的工伤接口返回数据
    
    -- 相关单据信息(用于单据管理)
    HisReceiptNo VARCHAR(50),                    -- HIS发票号:HIS系统生成的发票号
    WorkInjuryReceiptNo VARCHAR(50),             -- 工伤结算单号:工伤系统的结算单号
    HisPrescriptionNo VARCHAR(50),               -- HIS处方号:处方相关业务使用
    
    -- 时间信息
    BusinessTime DATETIME,                       -- 业务发生时间:实际业务办理时间
    
    -- 撤销和冲正信息(业务可能需要撤销)
    IsReversed BIT DEFAULT 0,                    -- 是否已冲正:0否,1是
    ReversedLogId BIGINT,                        -- 冲正操作的日志ID:关联到冲正接口调用记录
    ReversedTime DATETIME,                       -- 冲正时间:什么时候冲正的
    ReversedReason NVARCHAR(200),                -- 冲正原因:为什么要冲正
    
    -- 扩展字段
    Remark NVARCHAR(500),                        -- 备注:业务特殊说明
    ExtendData NTEXT,                            -- 扩展数据JSON:其他业务数据
    
    -- 创建和修改信息
    CreateTime DATETIME NOT NULL DEFAULT GETDATE(),
    UpdateTime DATETIME NOT NULL DEFAULT GETDATE()
);

-- 创建索引
CREATE INDEX IX_WorkInjury_Business_Data_LogId ON WorkInjury_Business_Data(LogId);
CREATE INDEX IX_WorkInjury_Business_Data_Patient ON WorkInjury_Business_Data(HisPatientId, BusinessTime);
CREATE INDEX IX_WorkInjury_Business_Data_Visit ON WorkInjury_Business_Data(HisVisitNo, BusinessType);
CREATE INDEX IX_WorkInjury_Business_Data_WorkInjury ON WorkInjury_Business_Data(WorkInjuryRegisterNo, WorkInjurySettleNo);
CREATE INDEX IX_WorkInjury_Business_Data_Business ON WorkInjury_Business_Data(BusinessType, BusinessStatus, BusinessTime);

3 表关联关系

3.1 表关系图

HIS病人表 (His_Patient)
     ↓ (1:1)
工伤病人关联表 (WorkInjury_Patient_Relation)  
     ↓ (1:N)
工伤接口调用记录表 (WorkInjury_Interface_Log)
     ↓ (1:N)  
工伤业务数据记录表 (WorkInjury_Business_Data)

3.2 关联说明

  1. HIS病人表 ← → 工伤病人关联表

    • 关联字段:HisPatientId
    • 关系:一对一,一个HIS病人对应一条工伤关联记录
    • 用途:判断病人是否为工伤性质
  2. 工伤病人关联表 → 工伤接口调用记录表

    • 关联字段:HisPatientId
    • 关系:一对多,一个病人可以有多次接口调用
    • 用途:追踪病人的所有工伤接口调用历史
  3. 工伤接口调用记录表 → 工伤业务数据记录表

    • 关联字段:LogId
    • 关系:一对多,一次接口调用可能产生多条业务数据
    • 用途:存储接口调用产生的具体业务数据

3.3 数据流向

病人挂号/就诊
       ↓
判断是否工伤病人 (查询WorkInjury_Patient_Relation)
       ↓ (是工伤)
调用工伤接口 → 记录到WorkInjury_Interface_Log
       ↓ (成功)
解析业务数据 → 存储到WorkInjury_Business_Data
       ↓
调用HIS接口 → 存储HIS业务数据

4 使用案例

4.1 典型业务场景案例

4.1.1 工伤病人门诊登记流程

场景描述:工伤病人来医院看门诊,需要先在工伤系统登记,再在HIS系统登记

数据操作流程

-- 1. 判断是否工伤病人
SELECT IsWorkInjuryPatient, WorkInjuryPsnNo, QualificationId 
FROM WorkInjury_Patient_Relation 
WHERE HisPatientId = 'P12345' AND Status = '1';

-- 如果是工伤病人,继续以下步骤:

-- 2. 调用工伤登记接口后,记录接口调用日志
INSERT INTO WorkInjury_Interface_Log (
    HisPatientId, HisVisitNo, InterfaceCode, InterfaceName, InterfaceCategory,
    RequestData, ResponseData, CallResult, ResultCode, ResultMessage,
    WorkInjuryMsgId, WorkInjuryQualificationId, CallTime, OperatorId, OperatorName
) VALUES (
    'P12345', 'MZ2024120100001', '2201', '门诊/住院登记', '业务类',
    '{"infno":"2201","psn_no":"32123456789","med_type":"11",...}',
    '{"infcode":"0","output":{"ipt_otp_no":"WS2024120100001"},...}',
    1, '0', '登记成功',
    'SQ201348202412011030001', 'WS2024120100001', GETDATE(), 'DOC001', '张医生'
);

-- 3. 记录业务数据
INSERT INTO WorkInjury_Business_Data (
    LogId, HisPatientId, HisVisitNo, BusinessType, BusinessCode, BusinessStatus,
    WorkInjuryRegisterNo, BusinessDetailData, BusinessTime
) VALUES (
    @@IDENTITY, 'P12345', 'MZ2024120100001', '登记', '2201', '成功',
    'WS2024120100001', '{"med_type":"11","adm_dept":"内科",...}', GETDATE()
);

4.1.2 工伤费用结算流程

场景描述:工伤病人看完病,需要先在工伤系统结算,确定报销金额,再在HIS收费

-- 1. 处方明细上传
INSERT INTO WorkInjury_Interface_Log (...) VALUES (...); -- 接口调用日志
INSERT INTO WorkInjury_Business_Data (
    LogId, HisPatientId, HisVisitNo, BusinessType, BusinessCode,
    BusinessDetailData, BusinessTime
) VALUES (
    @@IDENTITY, 'P12345', 'MZ2024120100001', '处方', '2204',
    '{"feedetail":[{"med_list_codg":"A01.01.001","med_name":"阿莫西林",...}]}',
    GETDATE()
);

-- 2. 费用预结算
INSERT INTO WorkInjury_Interface_Log (...) VALUES (...);
INSERT INTO WorkInjury_Business_Data (
    LogId, HisPatientId, HisVisitNo, BusinessType, BusinessCode,
    WorkInjuryPreSettleId, TotalFee, WorkInjuryFee, PersonalFee,
    BusinessDetailData, BusinessTime
) VALUES (
    @@IDENTITY, 'P12345', 'MZ2024120100001', '预结算', '2206',
    'PRE20241201001', 150.00, 120.00, 30.00,
    '{"pre_settle_data":{"psn_part_amt":30.00,"fund_pay_amt":120.00}}',
    GETDATE()
);

-- 3. 正式结算
INSERT INTO WorkInjury_Interface_Log (...) VALUES (...);
INSERT INTO WorkInjury_Business_Data (
    LogId, HisPatientId, HisVisitNo, BusinessType, BusinessCode,
    WorkInjurySettleNo, TotalFee, WorkInjuryFee, PersonalFee,
    WorkInjuryReceiptNo, BusinessTime
) VALUES (
    @@IDENTITY, 'P12345', 'MZ2024120100001', '结算', '2207',
    'SET20241201001', 150.00, 120.00, 30.00,
    'WS2024120100001', GETDATE()
);

4.1.3 对账类接口使用

场景描述:每日对账,下载工伤系统的结算汇总数据

-- 总额对账接口调用
INSERT INTO WorkInjury_Interface_Log (
    InterfaceCode, InterfaceName, InterfaceCategory,
    RequestData, ResponseData, CallResult,
    BusinessDetailData, CallTime, OperatorId
) VALUES (
    '1320', '总额对账', '对账类',
    '{"stmt_begndate":"20241201","stmt_enddate":"20241201"}',
    '{"infcode":"0","output":{"total_cnt":15,"total_amt":2250.00}}',
    1,
    '{"account_date":"20241201","total_count":15,"total_amount":2250.00}',
    GETDATE(), 'SYS001'
);

-- 对账业务数据记录
INSERT INTO WorkInjury_Business_Data (
    LogId, BusinessType, BusinessCode, BusinessStatus,
    TotalFee, BusinessDetailData, BusinessTime
) VALUES (
    @@IDENTITY, '对账', '1320', '成功',
    2250.00, '{"account_date":"20241201","detail_count":15}', GETDATE()
);

4.1.4 下载类接口使用

场景描述:下载费用明细数据,用于对账核实

-- 费用明细下载
INSERT INTO WorkInjury_Interface_Log (
    InterfaceCode, InterfaceName, InterfaceCategory,
    RequestData, ResponseData, CallResult,
    BusinessDetailData, CallTime
) VALUES (
    '9103', '费用明细详细信息下载', '下载类',
    '{"queryCond":{"stmt_begndate":"20241201","stmt_enddate":"20241201"}}',
    '{"infcode":"0","output":{"feedetail":[...]}}',
    1,
    '{"download_date":"20241201","record_count":156}',
    GETDATE()
);

5 前端示例代码

5.1 jQuery 示例 - 工伤病人登记

<!DOCTYPE html>
<html>
<head>
    <title>工伤病人登记</title>
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
</head>
<body>
    <div class="patient-register">
        <h3>工伤病人登记</h3>
        
        <!-- 病人信息 -->
        <div class="patient-info">
            <label>病人ID: <input type="text" id="patientId" value="P12345"></label>
            <label>就诊卡号: <input type="text" id="cardNo"></label>
            <label>病人姓名: <input type="text" id="patientName"></label>
            <button id="checkWorkInjury">检查工伤性质</button>
        </div>

        <!-- 工伤信息显示 -->
        <div id="workInjuryInfo" style="display:none;">
            <h4>工伤信息</h4>
            <div id="workInjuryDetail"></div>
            <button id="registerWorkInjury">工伤登记</button>
        </div>

        <!-- HIS登记 -->
        <div class="his-register">
            <h4>HIS系统登记</h4>
            <label>科室: <select id="department"><option value="001">内科</option></select></label>
            <label>医生: <select id="doctor"><option value="DOC001">张医生</option></select></label>
            <button id="registerHis">HIS登记</button>
        </div>

        <!-- 结果显示 -->
        <div id="result"></div>
    </div>

<script>
$(document).ready(function() {
    // 检查工伤性质
    $('#checkWorkInjury').click(function() {
        const patientId = $('#patientId').val();
        
        $.ajax({
            url: '/api/workinjury/checkPatient',
            method: 'POST',
            contentType: 'application/json',
            data: JSON.stringify({ patientId: patientId }),
            success: function(response) {
                if (response.success && response.data.isWorkInjuryPatient) {
                    // 是工伤病人,显示工伤信息
                    $('#workInjuryDetail').html(`
                        <p>工伤卡号: ${response.data.workInjuryCardNo}</p>
                        <p>个人识别码: ${response.data.workInjuryPsnNo}</p>
                        <p>资格状态: ${response.data.qualificationStatus}</p>
                        <p>有效期: ${response.data.qualificationExpireDate}</p>
                    `);
                    $('#workInjuryInfo').show();
                } else {
                    // 非工伤病人,直接显示HIS登记
                    $('#result').html('<div class="info">该病人非工伤性质,直接进行HIS登记</div>');
                }
            },
            error: function(xhr, status, error) {
                $('#result').html('<div class="error">检查失败: ' + error + '</div>');
            }
        });
    });

    // 工伤登记
    $('#registerWorkInjury').click(function() {
        const patientId = $('#patientId').val();
        const visitNo = 'MZ' + new Date().getFullYear() + 
                       (new Date().getMonth() + 1).toString().padStart(2, '0') + 
                       new Date().getDate().toString().padStart(2, '0') + '00001';
        
        const registerData = {
            action: "RegisterPatient",
            businessParams: {
                ipt_otp_no: visitNo,
                med_type: "11", // 门诊
                adm_time: new Date().toISOString().replace(/[-:]/g, '').slice(0, 14),
                adm_dept_codg: $('#department').val(),
                atddr_no: $('#doctor').val()
            },
            identifyMode: "1"
        };

        $.ajax({
            url: '/api/workinjury/transaction',
            method: 'POST',
            contentType: 'application/json',
            data: JSON.stringify(registerData),
            success: function(response) {
                if (response.success) {
                    $('#result').html('<div class="success">工伤登记成功,登记号: ' + 
                                    response.data.ipt_otp_no + '</div>');
                    
                    // 工伤登记成功后,自动进行HIS登记
                    $('#registerHis').click();
                } else {
                    $('#result').html('<div class="error">工伤登记失败: ' + response.message + '</div>');
                }
            }
        });
    });

    // HIS登记
    $('#registerHis').click(function() {
        const hisRegisterData = {
            patientId: $('#patientId').val(),
            patientName: $('#patientName').val(),
            department: $('#department').val(),
            doctor: $('#doctor').val(),
            visitType: '门诊'
        };

        $.ajax({
            url: '/api/his/register',
            method: 'POST',
            contentType: 'application/json',
            data: JSON.stringify(hisRegisterData),
            success: function(response) {
                if (response.success) {
                    $('#result').append('<div class="success">HIS登记成功,就诊号: ' + 
                                      response.data.visitNo + '</div>');
                } else {
                    $('#result').append('<div class="error">HIS登记失败: ' + response.message + '</div>');
                }
            }
        });
    });
});
</script>

<style>
.patient-register { padding: 20px; }
.patient-info, .his-register { margin: 15px 0; padding: 10px; border: 1px solid #ddd; }
label { display: block; margin: 5px 0; }
input, select { margin-left: 10px; padding: 5px; }
button { margin: 10px 5px; padding: 8px 15px; background: #007cba; color: white; border: none; cursor: pointer; }
.success { color: green; font-weight: bold; }
.error { color: red; font-weight: bold; }
.info { color: blue; font-weight: bold; }
</style>
</body>
</html>

5.2 Vue 3.0 示例 - 工伤费用结算

<template>
  <div class="work-injury-settlement">
    <h3>工伤费用结算</h3>
    
    <!-- 病人选择 -->
    <div class="patient-select">
      <el-form :model="patientForm" inline>
        <el-form-item label="就诊号">
          <el-input v-model="patientForm.visitNo" placeholder="请输入就诊号"></el-input>
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="loadPatientInfo">加载病人信息</el-button>
        </el-form-item>
      </el-form>
    </div>

    <!-- 病人信息显示 -->
    <div v-if="patientInfo.patientId" class="patient-info">
      <h4>病人信息</h4>
      <el-descriptions :column="3" border>
        <el-descriptions-item label="病人姓名">{{ patientInfo.patientName }}</el-descriptions-item>
        <el-descriptions-item label="就诊卡号">{{ patientInfo.cardNo }}</el-descriptions-item>
        <el-descriptions-item label="工伤性质">
          <el-tag :type="patientInfo.isWorkInjury ? 'success' : 'info'">
            {{ patientInfo.isWorkInjury ? '工伤病人' : '普通病人' }}
          </el-tag>
        </el-descriptions-item>
      </el-descriptions>
    </div>

    <!-- 处方明细 -->
    <div class="prescription-detail">
      <h4>处方明细</h4>
      <el-table :data="prescriptionList" border>
        <el-table-column prop="medName" label="药品名称"></el-table-column>
        <el-table-column prop="quantity" label="数量"></el-table-column>
        <el-table-column prop="unitPrice" label="单价"></el-table-column>
        <el-table-column prop="totalPrice" label="小计"></el-table-column>
      </el-table>
      <div class="total-fee">
        <strong>总金额: ¥{{ totalFee.toFixed(2) }}</strong>
      </div>
    </div>

    <!-- 结算操作 -->
    <div class="settlement-actions">
      <el-button v-if="patientInfo.isWorkInjury" 
                 type="warning" 
                 @click="uploadPrescription"
                 :loading="uploading">
        上传处方明细
      </el-button>
      
      <el-button v-if="patientInfo.isWorkInjury" 
                 type="info" 
                 @click="preSettle"
                 :loading="preSettling"
                 :disabled="!prescriptionUploaded">
        工伤预结算
      </el-button>
      
      <el-button type="success" 
                 @click="finalSettle"
                 :loading="settling">
        {{ patientInfo.isWorkInjury ? '工伤正式结算' : 'HIS收费结算' }}
      </el-button>
    </div>

    <!-- 结算结果 -->
    <div v-if="settlementResult" class="settlement-result">
      <h4>结算结果</h4>
      <el-card>
        <div v-if="patientInfo.isWorkInjury">
          <p><strong>工伤结算单号:</strong> {{ settlementResult.workInjurySettleNo }}</p>
          <p><strong>总费用:</strong> ¥{{ settlementResult.totalFee }}</p>
          <p><strong>工伤基金支付:</strong> ¥{{ settlementResult.workInjuryFee }}</p>
          <p><strong>个人支付:</strong> ¥{{ settlementResult.personalFee }}</p>
        </div>
        <div v-else>
          <p><strong>HIS收费单号:</strong> {{ settlementResult.hisReceiptNo }}</p>
          <p><strong>应收金额:</strong> ¥{{ settlementResult.totalFee }}</p>
        </div>
      </el-card>
    </div>

    <!-- 操作记录 -->
    <div class="operation-log">
      <h4>操作记录</h4>
      <el-timeline>
        <el-timeline-item 
          v-for="log in operationLogs" 
          :key="log.id"
          :timestamp="log.time"
          :type="log.success ? 'success' : 'danger'">
          {{ log.operation }}: {{ log.message }}
        </el-timeline-item>
      </el-timeline>
    </div>
  </div>
</template>

<script setup>
import { ref, reactive, computed } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'

// 响应式数据
const patientForm = reactive({
  visitNo: ''
})

const patientInfo = reactive({
  patientId: '',
  patientName: '',
  cardNo: '',
  isWorkInjury: false,
  workInjuryPsnNo: '',
  qualificationId: ''
})

const prescriptionList = ref([
  { medName: '阿莫西林胶囊', quantity: 2, unitPrice: 25.00, totalPrice: 50.00 },
  { medName: '布洛芬片', quantity: 1, unitPrice: 18.50, totalPrice: 18.50 },
  { medName: '维生素C片', quantity: 1, unitPrice: 12.80, totalPrice: 12.80 }
])

const uploading = ref(false)
const preSettling = ref(false)
const settling = ref(false)
const prescriptionUploaded = ref(false)
const settlementResult = ref(null)
const operationLogs = ref([])

// 计算属性
const totalFee = computed(() => {
  return prescriptionList.value.reduce((sum, item) => sum + item.totalPrice, 0)
})

// 方法
const loadPatientInfo = async () => {
  try {
    const response = await fetch('/api/workinjury/getPatientInfo', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ visitNo: patientForm.visitNo })
    })
    
    const result = await response.json()
    if (result.success) {
      Object.assign(patientInfo, result.data)
      addOperationLog('加载病人信息', '成功', true)
    } else {
      ElMessage.error('加载病人信息失败: ' + result.message)
      addOperationLog('加载病人信息', result.message, false)
    }
  } catch (error) {
    ElMessage.error('加载病人信息异常: ' + error.message)
    addOperationLog('加载病人信息', error.message, false)
  }
}

const uploadPrescription = async () => {
  uploading.value = true
  try {
    const prescriptionData = {
      action: "UploadPrescription",
      businessParams: {
        feedetail: prescriptionList.value.map(item => ({
          med_list_codg: item.medCode || 'A01.01.001',
          med_name: item.medName,
          med_qty: item.quantity,
          pric: item.unitPrice,
          det_item_fee_sumamt: item.totalPrice
        }))
      }
    }

    const response = await fetch('/api/workinjury/transaction', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(prescriptionData)
    })

    const result = await response.json()
    if (result.success) {
      prescriptionUploaded.value = true
      ElMessage.success('处方明细上传成功')
      addOperationLog('上传处方明细', '成功', true)
    } else {
      ElMessage.error('处方明细上传失败: ' + result.message)
      addOperationLog('上传处方明细', result.message, false)
    }
  } catch (error) {
    ElMessage.error('处方明细上传异常: ' + error.message)
    addOperationLog('上传处方明细', error.message, false)
  } finally {
    uploading.value = false
  }
}

const preSettle = async () => {
  preSettling.value = true
  try {
    const preSettleData = {
      action: "PreSettle",
      businessParams: {
        ipt_otp_no: patientForm.visitNo,
        psn_no: patientInfo.workInjuryPsnNo,
        total_amt: totalFee.value
      }
    }

    const response = await fetch('/api/workinjury/transaction', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(preSettleData)
    })

    const result = await response.json()
    if (result.success) {
      ElMessage.success('工伤预结算成功')
      addOperationLog('工伤预结算', 
        `个人支付: ¥${result.data.psn_part_amt}, 基金支付: ¥${result.data.fund_pay_amt}`, 
        true)
    } else {
      ElMessage.error('工伤预结算失败: ' + result.message)
      addOperationLog('工伤预结算', result.message, false)
    }
  } catch (error) {
    ElMessage.error('工伤预结算异常: ' + error.message)
    addOperationLog('工伤预结算', error.message, false)
  } finally {
    preSettling.value = false
  }
}

const finalSettle = async () => {
  settling.value = true
  try {
    if (patientInfo.isWorkInjury) {
      // 工伤正式结算
      const settleData = {
        action: "Settle",
        businessParams: {
          ipt_otp_no: patientForm.visitNo,
          psn_no: patientInfo.workInjuryPsnNo
        }
      }

      const response = await fetch('/api/workinjury/transaction', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(settleData)
      })

      const result = await response.json()
      if (result.success) {
        settlementResult.value = {
          workInjurySettleNo: result.data.setl_id,
          totalFee: result.data.medfee_sumamt,
          workInjuryFee: result.data.fund_pay_amt,
          personalFee: result.data.psn_part_amt
        }
        ElMessage.success('工伤结算成功')
        addOperationLog('工伤正式结算', '成功', true)

        // 工伤结算成功后,调用HIS收费
        await hisSettle()
      } else {
        ElMessage.error('工伤结算失败: ' + result.message)
        addOperationLog('工伤正式结算', result.message, false)
      }
    } else {
      // 直接HIS收费
      await hisSettle()
    }
  } catch (error) {
    ElMessage.error('结算异常: ' + error.message)
    addOperationLog('结算操作', error.message, false)
  } finally {
    settling.value = false
  }
}

const hisSettle = async () => {
  try {
    const hisSettleData = {
      visitNo: patientForm.visitNo,
      totalFee: patientInfo.isWorkInjury ? settlementResult.value?.personalFee : totalFee.value,
      paymentMethod: '现金'
    }

    const response = await fetch('/api/his/settle', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(hisSettleData)
    })

    const result = await response.json()
    if (result.success) {
      if (!settlementResult.value) {
        settlementResult.value = {}
      }
      settlementResult.value.hisReceiptNo = result.data.receiptNo
      if (!patientInfo.isWorkInjury) {
        settlementResult.value.totalFee = totalFee.value
      }
      
      ElMessage.success('HIS收费成功')
      addOperationLog('HIS收费', '成功', true)
    } else {
      ElMessage.error('HIS收费失败: ' + result.message)
      addOperationLog('HIS收费', result.message, false)
    }
  } catch (error) {
    ElMessage.error('HIS收费异常: ' + error.message)
    addOperationLog('HIS收费', error.message, false)
  }
}

const addOperationLog = (operation, message, success) => {
  operationLogs.value.unshift({
    id: Date.now(),
    operation,
    message,
    success,
    time: new Date().toLocaleString()
  })
}
</script>

<style scoped>
.work-injury-settlement {
  padding: 20px;
}

.patient-select, .patient-info, .prescription-detail, 
.settlement-actions, .settlement-result, .operation-log {
  margin: 20px 0;
}

.total-fee {
  text-align: right;
  margin: 10px 0;
  font-size: 16px;
}

.settlement-actions {
  text-align: center;
}

.settlement-actions .el-button {
  margin: 0 10px;
}
</style>

6 后端示例代码

6.1 Spring Boot 示例 - 工伤接口服务

package com.hospital.workinjury.service;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.hospital.workinjury.entity.*;
import com.hospital.workinjury.repository.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

/**
 * 工伤联网接口服务类
 * 负责工伤接口调用和数据存储的核心业务逻辑
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class WorkInjuryService {

    private final WorkInjuryInterfaceLogRepository logRepository;
    private final WorkInjuryPatientRelationRepository relationRepository;
    private final WorkInjuryBusinessDataRepository businessRepository;
    private final RestTemplate restTemplate;
    private final ObjectMapper objectMapper;
    
    // 工伤接口地址配置
    private static final String WORK_INJURY_API_URL = "http://localhost:8321/api/workinjury/transaction";

    /**
     * 检查病人工伤性质
     * @param patientId HIS病人ID
     * @return 工伤病人信息
     */
    public WorkInjuryPatientInfo checkWorkInjuryPatient(String patientId) {
        log.info("检查病人工伤性质, patientId: {}", patientId);
        
        WorkInjuryPatientRelation relation = relationRepository.findByHisPatientId(patientId);
        
        WorkInjuryPatientInfo info = new WorkInjuryPatientInfo();
        info.setPatientId(patientId);
        
        if (relation != null && relation.getIsWorkInjuryPatient()) {
            info.setWorkInjuryPatient(true);
            info.setWorkInjuryCardNo(relation.getWorkInjuryCardNo());
            info.setWorkInjuryPsnNo(relation.getWorkInjuryPsnNo());
            info.setQualificationId(relation.getQualificationId());
            info.setQualificationStatus(relation.getQualificationStatus());
            info.setQualificationExpireDate(relation.getQualificationExpireDate());
        } else {
            info.setWorkInjuryPatient(false);
        }
        
        return info;
    }

    /**
     * 工伤病人登记流程
     * 先调用工伤接口,成功后记录数据,再调用HIS接口
     */
    @Transactional
    public WorkInjuryRegisterResult registerWorkInjuryPatient(WorkInjuryRegisterRequest request) {
        log.info("开始工伤病人登记流程, patientId: {}, visitNo: {}", 
                request.getPatientId(), request.getVisitNo());
        
        WorkInjuryRegisterResult result = new WorkInjuryRegisterResult();
        
        try {
            // 1. 检查是否工伤病人
            WorkInjuryPatientRelation relation = relationRepository.findByHisPatientId(request.getPatientId());
            if (relation == null || !relation.getIsWorkInjuryPatient()) {
                // 非工伤病人,直接调用HIS登记
                return registerHisPatientOnly(request);
            }

            // 2. 调用工伤登记接口
            WorkInjuryApiResult workInjuryResult = callWorkInjuryRegisterApi(request, relation);
            
            // 3. 记录工伤接口调用日志
            Long logId = saveInterfaceLog(request, workInjuryResult, "2201", "门诊/住院登记", "业务类");
            
            if (workInjuryResult.isSuccess()) {
                // 4. 记录工伤业务数据
                saveBusinessData(logId, request, workInjuryResult, "登记", "2201");
                
                // 5. 调用HIS登记接口
                HisRegisterResult hisResult = callHisRegisterApi(request);
                
                // 6. 组装返回结果
                result.setSuccess(true);
                result.setMessage("工伤病人登记成功");
                result.setWorkInjuryRegisterNo(workInjuryResult.getData().get("ipt_otp_no").toString());
                result.setHisVisitNo(hisResult.getVisitNo());
                result.setQualificationId(workInjuryResult.getData().get("qualification_id").toString());
                
                log.info("工伤病人登记成功, 工伤登记号: {}, HIS就诊号: {}", 
                        result.getWorkInjuryRegisterNo(), result.getHisVisitNo());
            } else {
                result.setSuccess(false);
                result.setMessage("工伤登记失败: " + workInjuryResult.getMessage());
                log.error("工伤登记失败: {}", workInjuryResult.getMessage());
            }
            
        } catch (Exception e) {
            log.error("工伤病人登记异常", e);
            result.setSuccess(false);
            result.setMessage("登记异常: " + e.getMessage());
        }
        
        return result;
    }

    /**
     * 工伤费用结算流程
     * 处方上传 -> 预结算 -> 正式结算 -> HIS收费
     */
    @Transactional
    public WorkInjurySettlementResult settleWorkInjuryFee(WorkInjurySettlementRequest request) {
        log.info("开始工伤费用结算流程, visitNo: {}", request.getVisitNo());
        
        WorkInjurySettlementResult result = new WorkInjurySettlementResult();
        
        try {
            // 1. 检查病人工伤性质
            WorkInjuryPatientRelation relation = relationRepository.findByHisPatientId(request.getPatientId());
            if (relation == null || !relation.getIsWorkInjuryPatient()) {
                // 非工伤病人,直接HIS收费
                return settleHisFeeOnly(request);
            }

            // 2. 上传处方明细
            WorkInjuryApiResult uploadResult = uploadPrescriptionDetail(request);
            if (!uploadResult.isSuccess()) {
                result.setSuccess(false);
                result.setMessage("处方上传失败: " + uploadResult.getMessage());
                return result;
            }

            // 3. 费用预结算
            WorkInjuryApiResult preSettleResult = preSettleFee(request);
            if (!preSettleResult.isSuccess()) {
                result.setSuccess(false);
                result.setMessage("预结算失败: " + preSettleResult.getMessage());
                return result;
            }

            // 4. 正式结算
            WorkInjuryApiResult settleResult = settleFee(request);
            if (!settleResult.isSuccess()) {
                result.setSuccess(false);
                result.setMessage("工伤结算失败: " + settleResult.getMessage());
                return result;
            }

            // 5. 解析结算结果
            Map<String, Object> settleData = settleResult.getData();
            BigDecimal totalFee = new BigDecimal(settleData.get("medfee_sumamt").toString());
            BigDecimal workInjuryFee = new BigDecimal(settleData.get("fund_pay_amt").toString());
            BigDecimal personalFee = new BigDecimal(settleData.get("psn_part_amt").toString());

            // 6. 记录结算业务数据
            Long logId = saveInterfaceLog(request, settleResult, "2207", "费用结算", "业务类");
            saveSettlementBusinessData(logId, request, settleResult, totalFee, workInjuryFee, personalFee);

            // 7. HIS收费(只收个人支付部分)
            HisSettlementResult hisResult = callHisSettlementApi(request, personalFee);

            // 8. 组装返回结果
            result.setSuccess(true);
            result.setMessage("工伤费用结算成功");
            result.setWorkInjurySettleNo(settleData.get("setl_id").toString());
            result.setTotalFee(totalFee);
            result.setWorkInjuryFee(workInjuryFee);
            result.setPersonalFee(personalFee);
            result.setHisReceiptNo(hisResult.getReceiptNo());

            log.info("工伤费用结算成功, 结算号: {}, 总费用: {}, 个人支付: {}", 
                    result.getWorkInjurySettleNo(), totalFee, personalFee);

        } catch (Exception e) {
            log.error("工伤费用结算异常", e);
            result.setSuccess(false);
            result.setMessage("结算异常: " + e.getMessage());
        }
        
        return result;
    }

    /**
     * 对账接口调用
     * 用于每日对账核对数据
     */
    public WorkInjuryAccountResult dailyAccount(String accountDate) {
        log.info("开始工伤对账, 对账日期: {}", accountDate);
        
        WorkInjuryAccountResult result = new WorkInjuryAccountResult();
        
        try {
            // 1. 构造对账请求参数
            Map<String, Object> accountParams = new HashMap<>();
            accountParams.put("stmt_begndate", accountDate);
            accountParams.put("stmt_enddate", accountDate);

            WorkInjuryApiRequest apiRequest = new WorkInjuryApiRequest();
            apiRequest.setAction("TotalAccount");
            apiRequest.setBusinessParams(accountParams);

            // 2. 调用工伤总额对账接口
            WorkInjuryApiResult apiResult = callWorkInjuryApi(apiRequest);
            
            // 3. 记录接口调用日志
            Long logId = saveAccountInterfaceLog(apiRequest, apiResult, "1320", "总额对账", "对账类");
            
            if (apiResult.isSuccess()) {
                // 4. 解析对账结果
                Map<String, Object> accountData = apiResult.getData();
                int totalCount = Integer.parseInt(accountData.get("total_cnt").toString());
                BigDecimal totalAmount = new BigDecimal(accountData.get("total_amt").toString());
                
                // 5. 记录对账业务数据
                saveAccountBusinessData(logId, accountDate, totalCount, totalAmount);
                
                // 6. 组装返回结果
                result.setSuccess(true);
                result.setMessage("对账成功");
                result.setAccountDate(accountDate);
                result.setTotalCount(totalCount);
                result.setTotalAmount(totalAmount);
                
                log.info("工伤对账成功, 日期: {}, 笔数: {}, 金额: {}", accountDate, totalCount, totalAmount);
            } else {
                result.setSuccess(false);
                result.setMessage("对账失败: " + apiResult.getMessage());
                log.error("工伤对账失败: {}", apiResult.getMessage());
            }
            
        } catch (Exception e) {
            log.error("工伤对账异常", e);
            result.setSuccess(false);
            result.setMessage("对账异常: " + e.getMessage());
        }
        
        return result;
    }

    /**
     * 下载费用明细数据
     * 用于对账核实和数据分析
     */
    public WorkInjuryDownloadResult downloadFeeDetail(String beginDate, String endDate) {
        log.info("开始下载工伤费用明细, 开始日期: {}, 结束日期: {}", beginDate, endDate);
        
        WorkInjuryDownloadResult result = new WorkInjuryDownloadResult();
        
        try {
            // 1. 构造下载请求参数
            Map<String, Object> queryCondition = new HashMap<>();
            queryCondition.put("stmt_begndate", beginDate);
            queryCondition.put("stmt_enddate", endDate);
            
            Map<String, Object> downloadParams = new HashMap<>();
            downloadParams.put("queryCond", queryCondition);

            WorkInjuryApiRequest apiRequest = new WorkInjuryApiRequest();
            apiRequest.setAction("QueryFeeDetail");
            apiRequest.setBusinessParams(downloadParams);

            // 2. 调用工伤费用明细下载接口
            WorkInjuryApiResult apiResult = callWorkInjuryApi(apiRequest);
            
            // 3. 记录接口调用日志
            Long logId = saveDownloadInterfaceLog(apiRequest, apiResult, "9103", "费用明细详细信息下载", "下载类");
            
            if (apiResult.isSuccess()) {
                // 4. 解析下载结果
                Map<String, Object> downloadData = apiResult.getData();
                @SuppressWarnings("unchecked")
                java.util.List<Map<String, Object>> feeDetailList = 
                    (java.util.List<Map<String, Object>>) downloadData.get("feedetail");
                
                int recordCount = feeDetailList != null ? feeDetailList.size() : 0;
                
                // 5. 记录下载业务数据
                saveDownloadBusinessData(logId, beginDate, endDate, recordCount, feeDetailList);
                
                // 6. 组装返回结果
                result.setSuccess(true);
                result.setMessage("下载成功");
                result.setBeginDate(beginDate);
                result.setEndDate(endDate);
                result.setRecordCount(recordCount);
                result.setFeeDetailList(feeDetailList);
                
                log.info("工伤费用明细下载成功, 记录数: {}", recordCount);
            } else {
                result.setSuccess(false);
                result.setMessage("下载失败: " + apiResult.getMessage());
                log.error("工伤费用明细下载失败: {}", apiResult.getMessage());
            }
            
        } catch (Exception e) {
            log.error("工伤费用明细下载异常", e);
            result.setSuccess(false);
            result.setMessage("下载异常: " + e.getMessage());
        }
        
        return result;
    }

    // ==================== 私有辅助方法 ====================

    /**
     * 调用工伤登记接口
     */
    private WorkInjuryApiResult callWorkInjuryRegisterApi(
            WorkInjuryRegisterRequest request, 
            WorkInjuryPatientRelation relation) throws Exception {
        
        Map<String, Object> businessParams = new HashMap<>();
        businessParams.put("ipt_otp_no", request.getVisitNo());
        businessParams.put("med_type", "11"); // 门诊
        businessParams.put("adm_time", LocalDateTime.now().format(java.time.format.DateTimeFormatter.ofPattern("yyyyMMddHHmmss")));
        businessParams.put("adm_dept_codg", request.getDepartmentCode());
        businessParams.put("atddr_no", request.getDoctorCode());
        businessParams.put("psn_no", relation.getWorkInjuryPsnNo());
        businessParams.put("qualification_id", relation.getQualificationId());

        WorkInjuryApiRequest apiRequest = new WorkInjuryApiRequest();
        apiRequest.setAction("RegisterPatient");
        apiRequest.setBusinessParams(businessParams);
        apiRequest.setIdentifyMode("1");

        return callWorkInjuryApi(apiRequest);
    }

    /**
     * 统一的工伤接口调用方法
     */
    private WorkInjuryApiResult callWorkInjuryApi(WorkInjuryApiRequest request) throws Exception {
        log.debug("调用工伤接口, action: {}", request.getAction());
        
        ResponseEntity<Map> response = restTemplate.postForEntity(
            WORK_INJURY_API_URL, 
            request, 
            Map.class
        );
        
        Map<String, Object> responseBody = response.getBody();
        
        WorkInjuryApiResult result = new WorkInjuryApiResult();
        result.setSuccess((Boolean) responseBody.get("success"));
        result.setCode((String) responseBody.get("code"));
        result.setMessage((String) responseBody.get("message"));
        
        if (result.isSuccess()) {
            @SuppressWarnings("unchecked")
            Map<String, Object> data = (Map<String, Object>) responseBody.get("data");
            result.setData(data);
        }
        
        return result;
    }

    /**
     * 保存接口调用日志
     */
    private Long saveInterfaceLog(
            Object request, 
            WorkInjuryApiResult apiResult, 
            String interfaceCode,
            String interfaceName,
            String interfaceCategory) {
        
        try {
            WorkInjuryInterfaceLog log = new WorkInjuryInterfaceLog();
            
            // 设置基本信息
            if (request instanceof WorkInjuryRegisterRequest) {
                WorkInjuryRegisterRequest registerRequest = (WorkInjuryRegisterRequest) request;
                log.setHisPatientId(registerRequest.getPatientId());
                log.setHisVisitNo(registerRequest.getVisitNo());
            } else if (request instanceof WorkInjurySettlementRequest) {
                WorkInjurySettlementRequest settlementRequest = (WorkInjurySettlementRequest) request;
                log.setHisPatientId(settlementRequest.getPatientId());
                log.setHisVisitNo(settlementRequest.getVisitNo());
            }
            
            log.setInterfaceCode(interfaceCode);
            log.setInterfaceName(interfaceName);
            log.setInterfaceCategory(interfaceCategory);
            
            // 设置请求和响应数据
            log.setRequestData(objectMapper.writeValueAsString(request));
            log.setResponseData(objectMapper.writeValueAsString(apiResult));
            
            // 设置调用结果
            log.setCallResult(apiResult.isSuccess());
            log.setResultCode(apiResult.getCode());
            log.setResultMessage(apiResult.getMessage());
            
            // 设置工伤系统关键信息
            if (apiResult.isSuccess() && apiResult.getData() != null) {
                Map<String, Object> data = apiResult.getData();
                log.setWorkInjuryMsgId((String) data.get("inf_refmsgid"));
                log.setWorkInjuryQualificationId((String) data.get("qualification_id"));
            }
            
            // 设置时间和操作信息
            log.setCallTime(LocalDateTime.now());
            log.setOperatorId("SYSTEM");
            log.setOperatorName("系统自动");
            
            WorkInjuryInterfaceLog savedLog = logRepository.save(log);
            return savedLog.getLogId();
            
        } catch (Exception e) {
            log.error("保存接口调用日志失败", e);
            return null;
        }
    }

    /**
     * 保存业务数据
     */
    private void saveBusinessData(
            Long logId,
            WorkInjuryRegisterRequest request,
            WorkInjuryApiResult apiResult,
            String businessType,
            String businessCode) {
        
        try {
            WorkInjuryBusinessData businessData = new WorkInjuryBusinessData();
            
            businessData.setLogId(logId);
            businessData.setHisPatientId(request.getPatientId());
            businessData.setHisVisitNo(request.getVisitNo());
            businessData.setBusinessType(businessType);
            businessData.setBusinessCode(businessCode);
            businessData.setBusinessStatus("成功");
            
            if (apiResult.getData() != null) {
                Map<String, Object> data = apiResult.getData();
                businessData.setWorkInjuryRegisterNo((String) data.get("ipt_otp_no"));
                businessData.setBusinessDetailData(objectMapper.writeValueAsString(data));
            }
            
            businessData.setBusinessTime(LocalDateTime.now());
            
            businessRepository.save(businessData);
            
        } catch (Exception e) {
            log.error("保存业务数据失败", e);
        }
    }

    /**
     * 保存结算业务数据
     */
    private void saveSettlementBusinessData(
            Long logId,
            WorkInjurySettlementRequest request,
            WorkInjuryApiResult apiResult,
            BigDecimal totalFee,
            BigDecimal workInjuryFee,
            BigDecimal personalFee) {
        
        try {
            WorkInjuryBusinessData businessData = new WorkInjuryBusinessData();
            
            businessData.setLogId(logId);
            businessData.setHisPatientId(request.getPatientId());
            businessData.setHisVisitNo(request.getVisitNo());
            businessData.setBusinessType("结算");
            businessData.setBusinessCode("2207");
            businessData.setBusinessStatus("成功");
            
            // 设置费用信息
            businessData.setTotalFee(totalFee);
            businessData.setWorkInjuryFee(workInjuryFee);
            businessData.setPersonalFee(personalFee);
            
            if (apiResult.getData() != null) {
                Map<String, Object> data = apiResult.getData();
                businessData.setWorkInjurySettleNo((String) data.get("setl_id"));
                businessData.setWorkInjuryReceiptNo((String) data.get("setl_detail_id"));
                businessData.setBusinessDetailData(objectMapper.writeValueAsString(data));
            }
            
            businessData.setBusinessTime(LocalDateTime.now());
            
            businessRepository.save(businessData);
            
        } catch (Exception e) {
            log.error("保存结算业务数据失败", e);
        }
    }

    // 其他辅助方法... (uploadPrescriptionDetail, preSettleFee, settleFee, callHisApi等)
    // 为了节省篇幅,这里省略了部分辅助方法的实现
}

这个完整的文档涵盖了:

  1. 数据库表设计:3个通用表支持所有工伤接口类型
  2. 详细注释说明:每个字段都有明确的用途说明
  3. 表关联关系:清晰的关联逻辑和数据流向
  4. 使用案例:典型业务场景的数据操作示例
  5. 前端代码:jQuery和Vue 3.0的完整实现示例
  6. 后端代码:Spring Boot的服务层实现

这套设计方案能够完美实现您的需求:工伤病人先调用工伤接口存储数据,再调用HIS接口;非工伤病人直接调用HIS接口,同时通过通用化设计支持所有类型的工伤接口,简单实用可靠。