江苏工伤联网HTTP接口使用示例.md 110 KB

江苏工伤联网结算HTTP接口详细使用手册

📖 项目背景介绍

什么是江苏工伤联网结算?

江苏工伤联网结算是江苏省人社厅推出的工伤保险联网结算系统,允许工伤职工在江苏省内任何一家定点医疗机构就医时,都能直接刷卡结算,无需垫付医疗费用。

为什么需要这个接口?

医院的HIS系统需要与江苏工伤联网平台对接,实现:

  • 工伤职工身份识别(读卡)
  • 就医登记备案
  • 费用实时结算
  • 医保基金支付

本接口的作用

本HTTP接口是医院HIS系统与江苏工伤联网平台之间的桥梁,封装了复杂的工伤业务逻辑,提供简单易用的HTTP调用方式。


🔧 接口基础信息

服务地址

POST http://localhost:8321/api/entry/workinjury

请求头设置

Content-Type: application/json
Accept: application/json

通用参数说明

每个请求都必须包含 action 参数,用于指定要执行的操作:

action 值 功能描述 使用场景
init 系统初始化 程序启动时调用一次
transaction 业务交易 处理各种工伤业务(最常用)
health 健康检查 监控系统状态
stats 获取统计 查看交易统计信息
signin_status 签到状态 查询当前操作员签到状态
batch_upload 批量上传 批量上传处方明细
complete_process 完整流程 一键完成整个就医流程
retry 智能重试 重试失败的交易
reverse 交易冲正 撤销之前的交易

🚀 快速开始指南

第一步:系统初始化

在使用任何工伤功能之前,必须先初始化系统。

基础初始化(使用默认配置)

POST http://localhost:8321/api/entry/workinjury
Content-Type: application/json

{
  "action": "init"
}

生产环境初始化(使用医院实际配置)

POST http://localhost:8321/api/entry/workinjury
Content-Type: application/json

{
  "action": "init",
  "config": {
    "fixmedinsCode": "SQ201348",         // 医院在江苏人社的编号
    "fixmedinsName": "沭阳铭和医院",      // 医院名称
    "receiverSysCode": "JSYTH",              // 接收方系统代码(固定值)
    "interfaceVersion": "V2.1",              // 接口版本(固定值)
    "operatorType": "1",                     // 操作员类型:1-人工,2-自助终端
    "defaultOperator": "001",                // 默认操作员编号
    "defaultOperatorName": "收费员张三",      // 默认操作员姓名
    "logPath": "logs/workinjury/"           // 日志存储路径
  }
}

📋 参数说明:

  • fixmedinsCode:必须填写医院在江苏人社系统中的实际编号
  • fixmedinsName:医院的完整名称
  • defaultOperator:建议使用当前登录的收费员工号

✅ 成功响应:

{
  "success": true,
  "code": 200,
  "message": "江苏工伤联网系统初始化成功",
  "device": "江苏工伤联网接口",
  "timestamp": "2024-12-01 10:30:00",
  "version": "V2.1"
}

第二步:健康检查

初始化后,建议检查系统是否正常:

POST http://localhost:8321/api/entry/workinjury
Content-Type: application/json

{
  "action": "health"
}

✅ 健康状态良好的响应:

{
  "success": true,
  "code": 200,
  "message": "系统健康状态良好",
  "data": {
    "overallHealth": true,
    "checks": {
      "dllStatus": { "success": true, "message": "DLL文件正常" },
      "initStatus": { "initialized": true, "hasConfig": true },
      "signInStatus": { "signedIn": true, "signNo": "20241201001" }
    }
  }
}

💼 核心业务交易详解

🏥 完整的工伤就医业务流程

工伤职工就医的标准流程:读卡 → 登记 → 开处方/检查 → 预结算 → 结算

方式一:分步操作(推荐,便于错误处理)

1️⃣ 工伤职工读卡
POST http://localhost:8321/api/entry/workinjury
Content-Type: application/json

{
  "action": "transaction",
  "transactionName": "读卡",           // 可以用中文、英文或代码
  "identifyMode": "1",                // 1-实体社保卡,2-电子凭证
  "operatorId": "001",                // 当前操作员工号
  "operatorName": "收费员张三"         // 当前操作员姓名
}

📋 identifyMode 参数说明:

  • "1":使用实体社保卡(插卡读取)
  • "2":使用电子凭证(扫码读取,需要提供qrCodeInfo)

🎯 使用电子凭证读卡:

{
  "action": "transaction",
  "transactionName": "读卡",
  "identifyMode": "2",
  "qrCodeInfo": "患者提供的二维码数据"
}

✅ 读卡成功响应:

{
  "success": true,
  "code": 200,
  "message": "读卡成功",
  "data": {
    "psn_no": "32010219800101001X",      // 人员编号(身份证号)
    "psn_name": "张三",                   // 姓名
    "gend": "1",                         // 性别:1-男,2-女
    "brdy": "19800101",                  // 出生日期
    "naty": "01",                        // 民族
    "card_sn": "D12345678901234567",     // 社保卡卡号
    "insuplc_admdvs": "320000",          // 参保地行政区划
    "emp_name": "某某公司",               // 单位名称
    "insutype": "390",                   // 险种类型(390-工伤保险)
    "balc": "1000.00"                    // 账户余额
  }
}
2️⃣ 患者就医登记
POST http://localhost:8321/api/entry/workinjury
Content-Type: application/json

{
  "action": "transaction",
  "transactionName": "RegisterPatient",
  "businessParams": {
    "psn_no": "32010219800101001X",      // 从读卡结果获取
    "visit_type": "1",                   // 就诊类型:1-门诊,2-住院
    "dept_code": "001",                  // 科室编码
    "dept_name": "骨科",                 // 科室名称
    "doctor_code": "DOC001",             // 医生编码
    "doctor_name": "张主任",             // 医生姓名
    "diag_code": "M79.900",             // 诊断代码(ICD-10)
    "diag_name": "骨折",                // 诊断名称
    "visit_time": "2024-12-01 10:30:00" // 就诊时间
  },
  "identifyMode": "1"
}

📋 visit_type 参数说明:

  • "1":门诊就医
  • "2":住院治疗

✅ 登记成功响应:

{
  "success": true,
  "code": 200,
  "message": "患者登记成功",
  "data": {
    "visit_no": "V20241201001",          // 就诊号(重要!后续交易需要)
    "reg_time": "2024-12-01 10:30:00",  // 登记时间
    "psn_no": "32010219800101001X",      // 人员编号
    "psn_name": "张三"                   // 患者姓名
  }
}
3️⃣ 费用预结算

患者看病、开药、检查后,在收费前先进行预结算:

POST http://localhost:8321/api/entry/workinjury
Content-Type: application/json

{
  "action": "transaction",
  "transactionName": "PreSettle",
  "businessParams": {
    "visit_no": "V20241201001",          // 从登记结果获取
    "fee_details": [                     // 费用明细数组
      {
        "fee_ocur_time": "2024-12-01 10:30:00",  // 费用发生时间
        "med_list_codg": "001",                   // 医疗目录编码
        "med_list_name": "X光片检查",              // 项目名称
        "med_chrgitm_type": "4",                  // 收费项目类别:1-药品,2-诊疗,4-检查
        "fee_amt": 120.00,                        // 金额
        "cnt": 1,                                 // 数量
        "pric": 120.00,                          // 单价
        "spec": "胸部X光",                        // 规格
        "dept_code": "001",                       // 开单科室
        "dept_name": "骨科"                       // 科室名称
      },
      {
        "fee_ocur_time": "2024-12-01 10:35:00",
        "med_list_codg": "002",
        "med_list_name": "诊疗费",
        "med_chrgitm_type": "2",
        "fee_amt": 15.00,
        "cnt": 1,
        "pric": 15.00,
        "spec": "门诊诊疗",
        "dept_code": "001",
        "dept_name": "骨科"
      }
    ]
  }
}

📋 med_chrgitm_type 收费项目类别:

  • "1":药品费用
  • "2":诊疗项目费用
  • "3":服务设施费用
  • "4":医用材料费用

✅ 预结算成功响应:

{
  "success": true,
  "code": 200,
  "message": "预结算成功",
  "data": {
    "pre_settle_id": "PS20241201001",    // 预结算流水号(重要!)
    "total_amt": 135.00,                 // 总费用
    "fund_pay_amt": 108.00,              // 工伤基金支付
    "psn_pay_amt": 27.00,                // 个人支付
    "calc_time": "2024-12-01 10:40:00"   // 计算时间
  }
}
4️⃣ 正式结算

预结算成功后,进行正式结算:

POST http://localhost:8321/api/entry/workinjury
Content-Type: application/json

{
  "action": "transaction",
  "transactionName": "Settle",
  "businessParams": {
    "visit_no": "V20241201001",          // 就诊号
    "pre_settle_id": "PS20241201001"     // 从预结算结果获取
  }
}

✅ 结算成功响应:

{
  "success": true,
  "code": 200,
  "message": "结算成功",
  "data": {
    "settle_id": "S20241201001",         // 结算流水号
    "settle_time": "2024-12-01 10:45:00", // 结算时间
    "total_amt": 135.00,                 // 总费用
    "fund_pay_amt": 108.00,              // 基金支付
    "psn_pay_amt": 27.00,                // 个人支付
    "invoice_no": "INV20241201001",      // 发票号
    "receipt_data": "结算凭证数据"        // 可打印的凭证信息
  }
}

方式二:一键完成流程(适合简单场景)

如果您希望一次性完成整个就医流程,可以使用完整流程接口:

POST http://localhost:8321/api/entry/workinjury
Content-Type: application/json

{
  "action": "complete_process",
  "patientInfo": {
    "visit_type": "1",
    "dept_code": "001",
    "dept_name": "骨科",
    "doctor_code": "DOC001",
    "doctor_name": "张主任"
  },
  "feeDetails": [
    {
      "fee_ocur_time": "2024-12-01 10:30:00",
      "med_list_codg": "001",
      "med_list_name": "X光片检查",
      "fee_amt": 120.00,
      "cnt": 1
    }
  ]
}

✅ 完整流程响应:

{
  "success": true,
  "code": 200,
  "message": "工伤就医流程完成",
  "processSteps": [
    { "step": 1, "name": "读卡", "success": true, "data": {...} },
    { "step": 2, "name": "登记", "success": true, "data": {...} },
    { "step": 3, "name": "预结算", "success": true, "data": {...} },
    { "step": 4, "name": "结算", "success": true, "data": {...} }
  ]
}

🎯 特殊业务场景

💊 批量上传处方明细

当患者有大量药品需要上传时:

POST http://localhost:8321/api/entry/workinjury
Content-Type: application/json

{
  "action": "batch_upload",
  "patientId": "32010219800101001X",
  "visitNo": "V20241201001",
  "prescriptions": [
    {
      "med_list_codg": "MED001",
      "med_list_name": "阿司匹林肠溶片",
      "spec": "100mg*30片",
      "cnt": 2,
      "pric": 25.50,
      "total_amt": 51.00,
      "dept_code": "001",
      "dept_name": "骨科"
    },
    {
      "med_list_codg": "MED002", 
      "med_list_name": "布洛芬缓释胶囊",
      "spec": "0.3g*20粒",
      "cnt": 1,
      "pric": 18.80,
      "total_amt": 18.80,
      "dept_code": "001",
      "dept_name": "骨科"
    }
    // ...可以有很多条,系统会自动分批上传(每批50条)
  ]
}

🔄 智能重试机制

当网络不稳定导致交易失败时,可以使用智能重试:

POST http://localhost:8321/api/entry/workinjury
Content-Type: application/json

{
  "action": "retry",
  "transactionName": "PreSettle",
  "businessParams": {
    "visit_no": "V20241201001",
    "fee_details": [...]
  },
  "maxRetries": 3,                     // 最大重试次数
  "baseDelayMs": 1000                  // 基础延迟时间(毫秒)
}

↩️ 交易冲正(撤销)

当需要撤销之前的交易时:

POST http://localhost:8321/api/entry/workinjury
Content-Type: application/json

{
  "action": "reverse",
  "originalMessageId": "H0000120241201103000010001"  // 原交易的报文ID
}

❓ 如何获取原交易报文ID? 从原交易的响应中获取 infRefMsgId 字段。


📊 系统监控和管理

🔍 签到状态查询

POST http://localhost:8321/api/entry/workinjury
Content-Type: application/json

{
  "action": "signin_status"
}

✅ 响应示例:

{
  "success": true,
  "data": {
    "signedIn": true,                    // 是否已签到
    "signNo": "20241201001",             // 签到流水号
    "signInTime": "2024-12-01 09:00:00", // 签到时间
    "signInOperator": "001",             // 签到操作员
    "sessionDuration": "93分钟"          // 已签到时长
  }
}

📈 系统统计信息

POST http://localhost:8321/api/entry/workinjury
Content-Type: application/json

{
  "action": "stats"
}

⚠️ 错误处理完全指南

错误响应格式

所有错误响应都遵循统一格式:

{
  "success": false,
  "code": 1005,
  "message": "具体错误信息",
  "device": "江苏工伤联网接口"
}

🔢 错误码分类

错误码范围 错误类型 处理建议
1000-1099 系统级错误 检查网络连接、DLL文件、配置参数
2000-2999 DLL调用错误 检查动态库文件、驱动程序
3000-3999 业务处理错误 检查业务参数、工伤资格

🐛 常见错误及解决方案

错误1:系统未初始化

{
  "success": false,
  "code": 1003,
  "message": "江苏工伤联网系统未初始化"
}

🔧 解决方案: 先调用初始化接口

错误2:参数错误

{
  "success": false,
  "code": 1005,
  "message": "transactionName参数不能为空"
}

🔧 解决方案: 检查请求参数是否完整

错误3:读卡失败

{
  "success": false,
  "code": 2011,
  "message": "请插入社保卡"
}

🔧 解决方案: 提示患者正确插入社保卡

错误4:业务规则错误

{
  "success": false,
  "code": 3001,
  "message": "人员未参保工伤保险",
  "businessErrorCode": "1001"
}

🔧 解决方案: 该患者未参加工伤保险,不能使用工伤结算


💻 编程语言调用示例

JavaScript (前端/Node.js)

class WorkInjuryAPI {
    constructor(baseUrl = 'http://localhost:8321/api/entry/workinjury') {
        this.baseUrl = baseUrl;
    }

    // 通用请求方法
    async request(data) {
        const response = await fetch(this.baseUrl, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(data)
        });
        return await response.json();
    }

    // 初始化系统
    async init(config = null) {
        return await this.request({
            action: 'init',
            config: config
        });
    }

    // 工伤患者读卡
    async readCard(identifyMode = '1', qrCodeInfo = '') {
        return await this.request({
            action: 'transaction',
            transactionName: '读卡',
            identifyMode: identifyMode,
            qrCodeInfo: qrCodeInfo
        });
    }

    // 患者登记
    async registerPatient(patientData) {
        return await this.request({
            action: 'transaction',
            transactionName: 'RegisterPatient',
            businessParams: patientData
        });
    }

    // 费用预结算
    async preSettle(visitNo, feeDetails) {
        return await this.request({
            action: 'transaction',
            transactionName: 'PreSettle',
            businessParams: {
                visit_no: visitNo,
                fee_details: feeDetails
            }
        });
    }

    // 正式结算
    async settle(visitNo, preSettleId) {
        return await this.request({
            action: 'transaction',
            transactionName: 'Settle',
            businessParams: {
                visit_no: visitNo,
                pre_settle_id: preSettleId
            }
        });
    }
}

// 使用示例
const api = new WorkInjuryAPI();

async function handleWorkInjuryPatient() {
    try {
        // 1. 初始化
        const initResult = await api.init();
        if (!initResult.success) {
            throw new Error('初始化失败: ' + initResult.message);
        }

        // 2. 读卡
        const cardResult = await api.readCard();
        if (!cardResult.success) {
            throw new Error('读卡失败: ' + cardResult.message);
        }

        const patientData = cardResult.data;
        console.log('患者信息:', patientData.psn_name);

        // 3. 登记
        const registerData = {
            psn_no: patientData.psn_no,
            visit_type: '1',
            dept_code: '001',
            dept_name: '骨科',
            doctor_code: 'DOC001',
            doctor_name: '张主任'
        };

        const registerResult = await api.registerPatient(registerData);
        if (!registerResult.success) {
            throw new Error('登记失败: ' + registerResult.message);
        }

        const visitNo = registerResult.data.visit_no;
        console.log('登记成功,就诊号:', visitNo);

        // 4. 预结算(假设有费用明细)
        const feeDetails = [
            {
                fee_ocur_time: new Date().toISOString().slice(0, 19).replace('T', ' '),
                med_list_codg: '001',
                med_list_name: 'X光检查',
                fee_amt: 120.00,
                cnt: 1
            }
        ];

        const preResult = await api.preSettle(visitNo, feeDetails);
        if (!preResult.success) {
            throw new Error('预结算失败: ' + preResult.message);
        }

        console.log('预结算成功,个人支付:', preResult.data.psn_pay_amt);

        // 5. 正式结算
        const settleResult = await api.settle(visitNo, preResult.data.pre_settle_id);
        if (!settleResult.success) {
            throw new Error('结算失败: ' + settleResult.message);
        }

        console.log('结算成功!发票号:', settleResult.data.invoice_no);
        return settleResult;

    } catch (error) {
        console.error('工伤结算失败:', error.message);
        throw error;
    }
}

C# (.NET)

using System;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;

public class WorkInjuryApiClient
{
    private readonly HttpClient _httpClient;
    private readonly string _baseUrl;

    public WorkInjuryApiClient(string baseUrl = "http://localhost:8321/api/entry/workinjury")
    {
        _httpClient = new HttpClient();
        _baseUrl = baseUrl;
    }

    // 通用请求方法
    private async Task<T> PostAsync<T>(object requestData)
    {
        var json = JsonConvert.SerializeObject(requestData);
        var content = new StringContent(json, Encoding.UTF8, "application/json");
        
        var response = await _httpClient.PostAsync(_baseUrl, content);
        var responseJson = await response.Content.ReadAsStringAsync();
        
        return JsonConvert.DeserializeObject<T>(responseJson);
    }

    // 初始化系统
    public async Task<ApiResponse> InitializeAsync(object config = null)
    {
        var request = new { action = "init", config = config };
        return await PostAsync<ApiResponse>(request);
    }

    // 读卡
    public async Task<ApiResponse> ReadCardAsync(string identifyMode = "1", string qrCodeInfo = "")
    {
        var request = new 
        { 
            action = "transaction",
            transactionName = "读卡",
            identifyMode = identifyMode,
            qrCodeInfo = qrCodeInfo
        };
        return await PostAsync<ApiResponse>(request);
    }

    // 患者登记
    public async Task<ApiResponse> RegisterPatientAsync(object patientData)
    {
        var request = new 
        { 
            action = "transaction",
            transactionName = "RegisterPatient",
            businessParams = patientData
        };
        return await PostAsync<ApiResponse>(request);
    }

    // 预结算
    public async Task<ApiResponse> PreSettleAsync(string visitNo, object[] feeDetails)
    {
        var request = new 
        { 
            action = "transaction",
            transactionName = "PreSettle",
            businessParams = new { visit_no = visitNo, fee_details = feeDetails }
        };
        return await PostAsync<ApiResponse>(request);
    }

    // 正式结算
    public async Task<ApiResponse> SettleAsync(string visitNo, string preSettleId)
    {
        var request = new 
        { 
            action = "transaction",
            transactionName = "Settle",
            businessParams = new { visit_no = visitNo, pre_settle_id = preSettleId }
        };
        return await PostAsync<ApiResponse>(request);
    }
}

// 响应数据模型
public class ApiResponse
{
    public bool Success { get; set; }
    public int Code { get; set; }
    public string Message { get; set; }
    public object Data { get; set; }
}

// 使用示例
public class WorkInjuryService
{
    private readonly WorkInjuryApiClient _apiClient;

    public WorkInjuryService()
    {
        _apiClient = new WorkInjuryApiClient();
    }

    public async Task<bool> ProcessWorkInjuryPatientAsync()
    {
        try
        {
            // 1. 初始化
            var initResult = await _apiClient.InitializeAsync();
            if (!initResult.Success)
            {
                throw new Exception($"初始化失败: {initResult.Message}");
            }

            // 2. 读卡
            var cardResult = await _apiClient.ReadCardAsync();
            if (!cardResult.Success)
            {
                throw new Exception($"读卡失败: {cardResult.Message}");
            }

            // 3. 登记
            var patientData = new 
            {
                psn_no = "从读卡结果获取",
                visit_type = "1",
                dept_code = "001",
                dept_name = "骨科"
            };

            var registerResult = await _apiClient.RegisterPatientAsync(patientData);
            if (!registerResult.Success)
            {
                throw new Exception($"登记失败: {registerResult.Message}");
            }

            Console.WriteLine("工伤患者处理成功!");
            return true;
        }
        catch (Exception ex)
        {
            Console.WriteLine($"处理失败: {ex.Message}");
            return false;
        }
    }
}

Python

import requests
import json
from datetime import datetime

class WorkInjuryAPI:
    def __init__(self, base_url='http://localhost:8321/api/entry/workinjury'):
        self.base_url = base_url
        self.session = requests.Session()
        self.session.headers.update({
            'Content-Type': 'application/json'
        })
    
    def _request(self, data):
        """通用请求方法"""
        response = self.session.post(self.base_url, json=data)
        return response.json()
    
    def init(self, config=None):
        """初始化系统"""
        return self._request({
            'action': 'init',
            'config': config
        })
    
    def read_card(self, identify_mode='1', qr_code_info=''):
        """读卡"""
        return self._request({
            'action': 'transaction',
            'transactionName': '读卡',
            'identifyMode': identify_mode,
            'qrCodeInfo': qr_code_info
        })
    
    def register_patient(self, patient_data):
        """患者登记"""
        return self._request({
            'action': 'transaction',
            'transactionName': 'RegisterPatient',
            'businessParams': patient_data
        })
    
    def pre_settle(self, visit_no, fee_details):
        """预结算"""
        return self._request({
            'action': 'transaction',
            'transactionName': 'PreSettle',
            'businessParams': {
                'visit_no': visit_no,
                'fee_details': fee_details
            }
        })
    
    def settle(self, visit_no, pre_settle_id):
        """正式结算"""
        return self._request({
            'action': 'transaction',
            'transactionName': 'Settle',
            'businessParams': {
                'visit_no': visit_no,
                'pre_settle_id': pre_settle_id
            }
        })

# 使用示例
def process_work_injury_patient():
    api = WorkInjuryAPI()
    
    try:
        # 1. 初始化
        init_result = api.init()
        if not init_result['success']:
            raise Exception(f"初始化失败: {init_result['message']}")
        
        # 2. 读卡
        card_result = api.read_card()
        if not card_result['success']:
            raise Exception(f"读卡失败: {card_result['message']}")
        
        patient_data = card_result['data']
        print(f"患者信息: {patient_data['psn_name']}")
        
        # 3. 登记
        register_data = {
            'psn_no': patient_data['psn_no'],
            'visit_type': '1',
            'dept_code': '001',
            'dept_name': '骨科',
            'doctor_code': 'DOC001',
            'doctor_name': '张主任'
        }
        
        register_result = api.register_patient(register_data)
        if not register_result['success']:
            raise Exception(f"登记失败: {register_result['message']}")
        
        visit_no = register_result['data']['visit_no']
        print(f"登记成功,就诊号: {visit_no}")
        
        # 4. 预结算
        fee_details = [{
            'fee_ocur_time': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
            'med_list_codg': '001',
            'med_list_name': 'X光检查',
            'fee_amt': 120.00,
            'cnt': 1
        }]
        
        pre_result = api.pre_settle(visit_no, fee_details)
        if not pre_result['success']:
            raise Exception(f"预结算失败: {pre_result['message']}")
        
        print(f"预结算成功,个人支付: {pre_result['data']['psn_pay_amt']}")
        
        # 5. 正式结算
        settle_result = api.settle(visit_no, pre_result['data']['pre_settle_id'])
        if not settle_result['success']:
            raise Exception(f"结算失败: {settle_result['message']}")
        
        print(f"结算成功!发票号: {settle_result['data']['invoice_no']}")
        return True
        
    except Exception as e:
        print(f"工伤结算失败: {e}")
        return False

 if __name__ == '__main__':
     process_work_injury_patient()

Spring Boot (Java)

项目依赖配置 (pom.xml)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.0</version>
        <relativePath/>
    </parent>
    
    <groupId>com.hospital</groupId>
    <artifactId>workinjury-integration</artifactId>
    <version>1.0.0</version>
    <name>work-injury-integration</name>
    <description>江苏工伤联网结算集成服务</description>
    
    <properties>
        <java.version>8</java.version>
    </properties>
    
    <dependencies>
        <!-- Spring Boot Web Starter -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        
        <!-- Spring Boot Validation -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
        
        <!-- HTTP Client -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>
        
        <!-- JSON处理 -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
        </dependency>
        
        <!-- 日志 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
        </dependency>
        
        <!-- 测试 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

配置文件 (application.yml)

# 应用配置
server:
  port: 8080
  servlet:
    context-path: /his

spring:
  application:
    name: workinjury-integration
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: GMT+8

# 江苏工伤联网配置
workinjury:
  api:
    base-url: http://localhost:8321/api/entry/workinjury
    timeout: 30000  # 30秒超时
    retry:
      max-attempts: 3
      delay: 1000   # 1秒延迟
  hospital:
    code: SQ201348
    name: 沭阳铭和医院
    operator:
      default-id: "001"
      default-name: "系统管理员"

# 日志配置
logging:
  level:
    com.hospital.workinjury: DEBUG
    org.springframework.web: INFO
  pattern:
    console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
  file:
    name: logs/workinjury-integration.log

配置类

package com.hospital.workinjury.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.client.WebClient;
import java.time.Duration;

/**
 * 江苏工伤联网配置类
 */
@Configuration
public class WorkInjuryConfig {

    @Bean
    @ConfigurationProperties(prefix = "workinjury")
    public WorkInjuryProperties workInjuryProperties() {
        return new WorkInjuryProperties();
    }

    @Bean
    public WebClient workInjuryWebClient(WorkInjuryProperties properties) {
        return WebClient.builder()
                .baseUrl(properties.getApi().getBaseUrl())
                .codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(1024 * 1024))
                .build();
    }

    /**
     * 工伤联网配置属性
     */
    public static class WorkInjuryProperties {
        private Api api = new Api();
        private Hospital hospital = new Hospital();

        // getters and setters
        public Api getApi() { return api; }
        public void setApi(Api api) { this.api = api; }
        public Hospital getHospital() { return hospital; }
        public void setHospital(Hospital hospital) { this.hospital = hospital; }

        public static class Api {
            private String baseUrl;
            private int timeout = 30000;
            private Retry retry = new Retry();

            // getters and setters
            public String getBaseUrl() { return baseUrl; }
            public void setBaseUrl(String baseUrl) { this.baseUrl = baseUrl; }
            public int getTimeout() { return timeout; }
            public void setTimeout(int timeout) { this.timeout = timeout; }
            public Retry getRetry() { return retry; }
            public void setRetry(Retry retry) { this.retry = retry; }
        }

        public static class Retry {
            private int maxAttempts = 3;
            private long delay = 1000;

            // getters and setters
            public int getMaxAttempts() { return maxAttempts; }
            public void setMaxAttempts(int maxAttempts) { this.maxAttempts = maxAttempts; }
            public long getDelay() { return delay; }
            public void setDelay(long delay) { this.delay = delay; }
        }

        public static class Hospital {
            private String code;
            private String name;
            private Operator operator = new Operator();

            // getters and setters
            public String getCode() { return code; }
            public void setCode(String code) { this.code = code; }
            public String getName() { return name; }
            public void setName(String name) { this.name = name; }
            public Operator getOperator() { return operator; }
            public void setOperator(Operator operator) { this.operator = operator; }
        }

        public static class Operator {
            private String defaultId;
            private String defaultName;

            // getters and setters
            public String getDefaultId() { return defaultId; }
            public void setDefaultId(String defaultId) { this.defaultId = defaultId; }
            public String getDefaultName() { return defaultName; }
            public void setDefaultName(String defaultName) { this.defaultName = defaultName; }
        }
    }
}

数据传输对象 (DTO)

package com.hospital.workinjury.dto;

import com.fasterxml.jackson.annotation.JsonProperty;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.util.List;

/**
 * 工伤联网API请求响应DTO
 */
public class WorkInjuryDto {

    /**
     * 通用API响应
     */
    public static class ApiResponse {
        private boolean success;
        private int code;
        private String message;
        private String device;
        private Object data;

        // constructors, getters and setters
        public ApiResponse() {}

        public boolean isSuccess() { return success; }
        public void setSuccess(boolean success) { this.success = success; }
        public int getCode() { return code; }
        public void setCode(int code) { this.code = code; }
        public String getMessage() { return message; }
        public void setMessage(String message) { this.message = message; }
        public String getDevice() { return device; }
        public void setDevice(String device) { this.device = device; }
        public Object getData() { return data; }
        public void setData(Object data) { this.data = data; }
    }

    /**
     * 患者读卡信息
     */
    public static class PatientCardInfo {
        @JsonProperty("psn_no")
        private String psnNo;           // 人员编号
        
        @JsonProperty("psn_name")
        private String psnName;         // 姓名
        
        @JsonProperty("gend")
        private String gender;          // 性别
        
        @JsonProperty("brdy")
        private String birthday;        // 出生日期
        
        @JsonProperty("card_sn")
        private String cardSn;          // 社保卡号
        
        @JsonProperty("emp_name")
        private String empName;         // 单位名称

        // getters and setters
        public String getPsnNo() { return psnNo; }
        public void setPsnNo(String psnNo) { this.psnNo = psnNo; }
        public String getPsnName() { return psnName; }
        public void setPsnName(String psnName) { this.psnName = psnName; }
        public String getGender() { return gender; }
        public void setGender(String gender) { this.gender = gender; }
        public String getBirthday() { return birthday; }
        public void setBirthday(String birthday) { this.birthday = birthday; }
        public String getCardSn() { return cardSn; }
        public void setCardSn(String cardSn) { this.cardSn = cardSn; }
        public String getEmpName() { return empName; }
        public void setEmpName(String empName) { this.empName = empName; }
    }

    /**
     * 患者登记请求
     */
    public static class PatientRegisterRequest {
        @JsonProperty("psn_no")
        @NotBlank(message = "人员编号不能为空")
        private String psnNo;

        @JsonProperty("visit_type")
        @NotBlank(message = "就诊类型不能为空")
        private String visitType;       // 1-门诊,2-住院

        @JsonProperty("dept_code")
        @NotBlank(message = "科室编码不能为空")
        private String deptCode;

        @JsonProperty("dept_name")
        @NotBlank(message = "科室名称不能为空")
        private String deptName;

        @JsonProperty("doctor_code")
        @NotBlank(message = "医生编码不能为空")
        private String doctorCode;

        @JsonProperty("doctor_name")
        @NotBlank(message = "医生姓名不能为空")
        private String doctorName;

        @JsonProperty("diag_code")
        private String diagCode;        // 诊断代码

        @JsonProperty("diag_name")
        private String diagName;        // 诊断名称

        @JsonProperty("visit_time")
        private String visitTime;       // 就诊时间

        // getters and setters
        public String getPsnNo() { return psnNo; }
        public void setPsnNo(String psnNo) { this.psnNo = psnNo; }
        public String getVisitType() { return visitType; }
        public void setVisitType(String visitType) { this.visitType = visitType; }
        public String getDeptCode() { return deptCode; }
        public void setDeptCode(String deptCode) { this.deptCode = deptCode; }
        public String getDeptName() { return deptName; }
        public void setDeptName(String deptName) { this.deptName = deptName; }
        public String getDoctorCode() { return doctorCode; }
        public void setDoctorCode(String doctorCode) { this.doctorCode = doctorCode; }
        public String getDoctorName() { return doctorName; }
        public void setDoctorName(String doctorName) { this.doctorName = doctorName; }
        public String getDiagCode() { return diagCode; }
        public void setDiagCode(String diagCode) { this.diagCode = diagCode; }
        public String getDiagName() { return diagName; }
        public void setDiagName(String diagName) { this.diagName = diagName; }
        public String getVisitTime() { return visitTime; }
        public void setVisitTime(String visitTime) { this.visitTime = visitTime; }
    }

    /**
     * 费用明细
     */
    public static class FeeDetail {
        @JsonProperty("fee_ocur_time")
        @NotBlank(message = "费用发生时间不能为空")
        private String feeOcurTime;

        @JsonProperty("med_list_codg")
        @NotBlank(message = "医疗目录编码不能为空")
        private String medListCodg;

        @JsonProperty("med_list_name")
        @NotBlank(message = "项目名称不能为空")
        private String medListName;

        @JsonProperty("med_chrgitm_type")
        @NotBlank(message = "收费项目类别不能为空")
        private String medChrgitmType;  // 1-药品,2-诊疗,3-服务,4-材料

        @JsonProperty("fee_amt")
        @NotNull(message = "费用金额不能为空")
        private BigDecimal feeAmt;

        @JsonProperty("cnt")
        @NotNull(message = "数量不能为空")
        private Integer cnt;

        @JsonProperty("pric")
        @NotNull(message = "单价不能为空")
        private BigDecimal pric;

        @JsonProperty("spec")
        private String spec;            // 规格

        @JsonProperty("dept_code")
        private String deptCode;        // 开单科室

        @JsonProperty("dept_name")
        private String deptName;        // 科室名称

        // getters and setters
        public String getFeeOcurTime() { return feeOcurTime; }
        public void setFeeOcurTime(String feeOcurTime) { this.feeOcurTime = feeOcurTime; }
        public String getMedListCodg() { return medListCodg; }
        public void setMedListCodg(String medListCodg) { this.medListCodg = medListCodg; }
        public String getMedListName() { return medListName; }
        public void setMedListName(String medListName) { this.medListName = medListName; }
        public String getMedChrgitmType() { return medChrgitmType; }
        public void setMedChrgitmType(String medChrgitmType) { this.medChrgitmType = medChrgitmType; }
        public BigDecimal getFeeAmt() { return feeAmt; }
        public void setFeeAmt(BigDecimal feeAmt) { this.feeAmt = feeAmt; }
        public Integer getCnt() { return cnt; }
        public void setCnt(Integer cnt) { this.cnt = cnt; }
        public BigDecimal getPric() { return pric; }
        public void setPric(BigDecimal pric) { this.pric = pric; }
        public String getSpec() { return spec; }
        public void setSpec(String spec) { this.spec = spec; }
        public String getDeptCode() { return deptCode; }
        public void setDeptCode(String deptCode) { this.deptCode = deptCode; }
        public String getDeptName() { return deptName; }
        public void setDeptName(String deptName) { this.deptName = deptName; }
    }

    /**
     * 结算信息
     */
    public static class SettlementInfo {
        @JsonProperty("settle_id")
        private String settleId;        // 结算流水号

        @JsonProperty("total_amt")
        private BigDecimal totalAmt;    // 总费用

        @JsonProperty("fund_pay_amt")
        private BigDecimal fundPayAmt;  // 基金支付

        @JsonProperty("psn_pay_amt")
        private BigDecimal psnPayAmt;   // 个人支付

        @JsonProperty("invoice_no")
        private String invoiceNo;       // 发票号

        // getters and setters
        public String getSettleId() { return settleId; }
        public void setSettleId(String settleId) { this.settleId = settleId; }
        public BigDecimal getTotalAmt() { return totalAmt; }
        public void setTotalAmt(BigDecimal totalAmt) { this.totalAmt = totalAmt; }
        public BigDecimal getFundPayAmt() { return fundPayAmt; }
        public void setFundPayAmt(BigDecimal fundPayAmt) { this.fundPayAmt = fundPayAmt; }
        public BigDecimal getPsnPayAmt() { return psnPayAmt; }
        public void setPsnPayAmt(BigDecimal psnPayAmt) { this.psnPayAmt = psnPayAmt; }
        public String getInvoiceNo() { return invoiceNo; }
        public void setInvoiceNo(String invoiceNo) { this.invoiceNo = invoiceNo; }
    }
}

服务层实现

package com.hospital.workinjury.service;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.hospital.workinjury.config.WorkInjuryConfig;
import com.hospital.workinjury.dto.WorkInjuryDto;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.client.WebClientResponseException;
import reactor.core.publisher.Mono;
import reactor.util.retry.Retry;

import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 江苏工伤联网业务服务
 */
@Service
public class WorkInjuryService {

    private static final Logger logger = LoggerFactory.getLogger(WorkInjuryService.class);
    private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    @Autowired
    private WebClient workInjuryWebClient;

    @Autowired
    private WorkInjuryConfig.WorkInjuryProperties workInjuryProperties;

    @Autowired
    private ObjectMapper objectMapper;

    private volatile boolean isInitialized = false;

    /**
     * 初始化工伤系统
     */
    public WorkInjuryDto.ApiResponse initialize() {
        try {
            Map<String, Object> request = new HashMap<>();
            request.put("action", "init");
            
            Map<String, Object> config = new HashMap<>();
            config.put("fixmedinsCode", workInjuryProperties.getHospital().getCode());
            config.put("fixmedinsName", workInjuryProperties.getHospital().getName());
            config.put("receiverSysCode", "JSYTH");
            config.put("interfaceVersion", "V2.1");
            config.put("operatorType", "1");
            config.put("defaultOperator", workInjuryProperties.getHospital().getOperator().getDefaultId());
            config.put("defaultOperatorName", workInjuryProperties.getHospital().getOperator().getDefaultName());
            config.put("logPath", "logs/workinjury/");
            
            request.put("config", config);

            WorkInjuryDto.ApiResponse response = callWorkInjuryApi(request);
            
            if (response.isSuccess()) {
                isInitialized = true;
                logger.info("江苏工伤联网系统初始化成功");
            } else {
                logger.error("江苏工伤联网系统初始化失败: {}", response.getMessage());
            }
            
            return response;
            
        } catch (Exception e) {
            logger.error("工伤系统初始化异常", e);
            return createErrorResponse(1001, "系统初始化异常: " + e.getMessage());
        }
    }

    /**
     * 检查系统健康状态
     */
    public WorkInjuryDto.ApiResponse healthCheck() {
        try {
            Map<String, Object> request = new HashMap<>();
            request.put("action", "health");
            
            return callWorkInjuryApi(request);
            
        } catch (Exception e) {
            logger.error("健康检查异常", e);
            return createErrorResponse(1002, "健康检查异常: " + e.getMessage());
        }
    }

    /**
     * 工伤患者读卡
     */
    public WorkInjuryDto.ApiResponse readCard(String identifyMode, String qrCodeInfo, String operatorId, String operatorName) {
        ensureInitialized();
        
        try {
            Map<String, Object> request = new HashMap<>();
            request.put("action", "transaction");
            request.put("transactionName", "读卡");
            request.put("identifyMode", identifyMode != null ? identifyMode : "1");
            
            if (qrCodeInfo != null && !qrCodeInfo.isEmpty()) {
                request.put("qrCodeInfo", qrCodeInfo);
            }
            
            if (operatorId != null && !operatorId.isEmpty()) {
                request.put("operatorId", operatorId);
                request.put("operatorName", operatorName != null ? operatorName : "");
            }

            WorkInjuryDto.ApiResponse response = callWorkInjuryApi(request);
            
            if (response.isSuccess()) {
                logger.info("工伤患者读卡成功");
            }
            
            return response;
            
        } catch (Exception e) {
            logger.error("读卡操作异常", e);
            return createErrorResponse(2001, "读卡操作异常: " + e.getMessage());
        }
    }

    /**
     * 患者登记
     */
    public WorkInjuryDto.ApiResponse registerPatient(WorkInjuryDto.PatientRegisterRequest registerRequest) {
        ensureInitialized();
        
        try {
            // 设置就诊时间(如果为空)
            if (registerRequest.getVisitTime() == null || registerRequest.getVisitTime().isEmpty()) {
                registerRequest.setVisitTime(LocalDateTime.now().format(DATE_TIME_FORMATTER));
            }
            
            Map<String, Object> request = new HashMap<>();
            request.put("action", "transaction");
            request.put("transactionName", "RegisterPatient");
            request.put("businessParams", registerRequest);
            request.put("identifyMode", "1");

            WorkInjuryDto.ApiResponse response = callWorkInjuryApi(request);
            
            if (response.isSuccess()) {
                logger.info("患者登记成功,患者编号: {}", registerRequest.getPsnNo());
            }
            
            return response;
            
        } catch (Exception e) {
            logger.error("患者登记异常", e);
            return createErrorResponse(3001, "患者登记异常: " + e.getMessage());
        }
    }

    /**
     * 费用预结算
     */
    public WorkInjuryDto.ApiResponse preSettle(String visitNo, List<WorkInjuryDto.FeeDetail> feeDetails) {
        ensureInitialized();
        
        try {
            Map<String, Object> businessParams = new HashMap<>();
            businessParams.put("visit_no", visitNo);
            businessParams.put("fee_details", feeDetails);
            
            Map<String, Object> request = new HashMap<>();
            request.put("action", "transaction");
            request.put("transactionName", "PreSettle");
            request.put("businessParams", businessParams);

            WorkInjuryDto.ApiResponse response = callWorkInjuryApi(request);
            
            if (response.isSuccess()) {
                logger.info("费用预结算成功,就诊号: {}", visitNo);
            }
            
            return response;
            
        } catch (Exception e) {
            logger.error("费用预结算异常", e);
            return createErrorResponse(3002, "费用预结算异常: " + e.getMessage());
        }
    }

    /**
     * 正式结算
     */
    public WorkInjuryDto.ApiResponse settle(String visitNo, String preSettleId) {
        ensureInitialized();
        
        try {
            Map<String, Object> businessParams = new HashMap<>();
            businessParams.put("visit_no", visitNo);
            businessParams.put("pre_settle_id", preSettleId);
            
            Map<String, Object> request = new HashMap<>();
            request.put("action", "transaction");
            request.put("transactionName", "Settle");
            request.put("businessParams", businessParams);

            WorkInjuryDto.ApiResponse response = callWorkInjuryApi(request);
            
            if (response.isSuccess()) {
                logger.info("正式结算成功,就诊号: {}", visitNo);
            }
            
            return response;
            
        } catch (Exception e) {
            logger.error("正式结算异常", e);
            return createErrorResponse(3003, "正式结算异常: " + e.getMessage());
        }
    }

    /**
     * 完整就医流程
     */
    public WorkInjuryDto.ApiResponse completeProcess(WorkInjuryDto.PatientRegisterRequest patientInfo, 
                                                    List<WorkInjuryDto.FeeDetail> feeDetails) {
        ensureInitialized();
        
        try {
            Map<String, Object> request = new HashMap<>();
            request.put("action", "complete_process");
            request.put("patientInfo", patientInfo);
            request.put("feeDetails", feeDetails);

            WorkInjuryDto.ApiResponse response = callWorkInjuryApi(request);
            
            if (response.isSuccess()) {
                logger.info("完整就医流程成功");
            }
            
            return response;
            
        } catch (Exception e) {
            logger.error("完整就医流程异常", e);
            return createErrorResponse(3004, "完整就医流程异常: " + e.getMessage());
        }
    }

    /**
     * 批量上传处方明细
     */
    public WorkInjuryDto.ApiResponse batchUploadPrescriptions(String patientId, String visitNo, 
                                                             List<Map<String, Object>> prescriptions) {
        ensureInitialized();
        
        try {
            Map<String, Object> request = new HashMap<>();
            request.put("action", "batch_upload");
            request.put("patientId", patientId);
            request.put("visitNo", visitNo);
            request.put("prescriptions", prescriptions);

            WorkInjuryDto.ApiResponse response = callWorkInjuryApi(request);
            
            if (response.isSuccess()) {
                logger.info("批量上传处方成功,患者: {}, 就诊号: {}", patientId, visitNo);
            }
            
            return response;
            
        } catch (Exception e) {
            logger.error("批量上传处方异常", e);
            return createErrorResponse(3005, "批量上传处方异常: " + e.getMessage());
        }
    }

    /**
     * 获取签到状态
     */
    public WorkInjuryDto.ApiResponse getSignInStatus() {
        try {
            Map<String, Object> request = new HashMap<>();
            request.put("action", "signin_status");
            
            return callWorkInjuryApi(request);
            
        } catch (Exception e) {
            logger.error("获取签到状态异常", e);
            return createErrorResponse(1003, "获取签到状态异常: " + e.getMessage());
        }
    }

    /**
     * 智能重试交易
     */
    public WorkInjuryDto.ApiResponse retryTransaction(String transactionName, Object businessParams, 
                                                     Integer maxRetries, Integer baseDelayMs) {
        ensureInitialized();
        
        try {
            Map<String, Object> request = new HashMap<>();
            request.put("action", "retry");
            request.put("transactionName", transactionName);
            request.put("businessParams", businessParams);
            
            if (maxRetries != null) {
                request.put("maxRetries", maxRetries);
            }
            if (baseDelayMs != null) {
                request.put("baseDelayMs", baseDelayMs);
            }

            return callWorkInjuryApi(request);
            
        } catch (Exception e) {
            logger.error("智能重试异常", e);
            return createErrorResponse(3006, "智能重试异常: " + e.getMessage());
        }
    }

    /**
     * 交易冲正
     */
    public WorkInjuryDto.ApiResponse reverseTransaction(String originalMessageId) {
        ensureInitialized();
        
        try {
            Map<String, Object> request = new HashMap<>();
            request.put("action", "reverse");
            request.put("originalMessageId", originalMessageId);

            return callWorkInjuryApi(request);
            
        } catch (Exception e) {
            logger.error("交易冲正异常", e);
            return createErrorResponse(3007, "交易冲正异常: " + e.getMessage());
        }
    }

    /**
     * 调用工伤联网API的通用方法
     */
    private WorkInjuryDto.ApiResponse callWorkInjuryApi(Map<String, Object> request) {
        try {
            logger.debug("调用工伤API,请求参数: {}", objectMapper.writeValueAsString(request));

            String responseBody = workInjuryWebClient
                    .post()
                    .bodyValue(request)
                    .retrieve()
                    .bodyToMono(String.class)
                    .retryWhen(Retry.backoff(
                            workInjuryProperties.getApi().getRetry().getMaxAttempts(),
                            Duration.ofMillis(workInjuryProperties.getApi().getRetry().getDelay())
                    ))
                    .timeout(Duration.ofMillis(workInjuryProperties.getApi().getTimeout()))
                    .block();

            logger.debug("工伤API响应: {}", responseBody);

            return objectMapper.readValue(responseBody, WorkInjuryDto.ApiResponse.class);

        } catch (WebClientResponseException e) {
            logger.error("工伤API调用HTTP异常,状态码: {}, 响应体: {}", e.getStatusCode(), e.getResponseBodyAsString());
            return createErrorResponse(2000 + e.getStatusCode().value(), 
                    "API调用失败,HTTP状态码: " + e.getStatusCode());
        } catch (Exception e) {
            logger.error("工伤API调用异常", e);
            return createErrorResponse(2000, "API调用异常: " + e.getMessage());
        }
    }

    /**
     * 确保系统已初始化
     */
    private void ensureInitialized() {
        if (!isInitialized) {
            WorkInjuryDto.ApiResponse initResult = initialize();
            if (!initResult.isSuccess()) {
                throw new RuntimeException("系统未初始化且自动初始化失败: " + initResult.getMessage());
            }
        }
    }

    /**
     * 创建错误响应
     */
    private WorkInjuryDto.ApiResponse createErrorResponse(int code, String message) {
        WorkInjuryDto.ApiResponse response = new WorkInjuryDto.ApiResponse();
        response.setSuccess(false);
        response.setCode(code);
        response.setMessage(message);
        response.setDevice("江苏工伤联网接口");
        return response;
    }
}

控制器层实现

package com.hospital.workinjury.controller;

import com.hospital.workinjury.dto.WorkInjuryDto;
import com.hospital.workinjury.service.WorkInjuryService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.util.List;
import java.util.Map;

/**
 * 江苏工伤联网结算控制器
 */
@RestController
@RequestMapping("/api/workinjury")
@Validated
public class WorkInjuryController {

    private static final Logger logger = LoggerFactory.getLogger(WorkInjuryController.class);

    @Autowired
    private WorkInjuryService workInjuryService;

    /**
     * 系统初始化
     */
    @PostMapping("/init")
    public ResponseEntity<WorkInjuryDto.ApiResponse> initialize() {
        logger.info("收到系统初始化请求");
        WorkInjuryDto.ApiResponse response = workInjuryService.initialize();
        return ResponseEntity.ok(response);
    }

    /**
     * 健康检查
     */
    @GetMapping("/health")
    public ResponseEntity<WorkInjuryDto.ApiResponse> healthCheck() {
        WorkInjuryDto.ApiResponse response = workInjuryService.healthCheck();
        return ResponseEntity.ok(response);
    }

    /**
     * 工伤患者读卡
     */
    @PostMapping("/readcard")
    public ResponseEntity<WorkInjuryDto.ApiResponse> readCard(
            @RequestParam(defaultValue = "1") String identifyMode,
            @RequestParam(required = false) String qrCodeInfo,
            @RequestParam(required = false) String operatorId,
            @RequestParam(required = false) String operatorName) {
        
        logger.info("收到读卡请求,识别方式: {}", identifyMode);
        WorkInjuryDto.ApiResponse response = workInjuryService.readCard(identifyMode, qrCodeInfo, operatorId, operatorName);
        return ResponseEntity.ok(response);
    }

    /**
     * 患者登记
     */
    @PostMapping("/register")
    public ResponseEntity<WorkInjuryDto.ApiResponse> registerPatient(
            @Valid @RequestBody WorkInjuryDto.PatientRegisterRequest request) {
        
        logger.info("收到患者登记请求,患者编号: {}", request.getPsnNo());
        WorkInjuryDto.ApiResponse response = workInjuryService.registerPatient(request);
        return ResponseEntity.ok(response);
    }

    /**
     * 费用预结算
     */
    @PostMapping("/presettle")
    public ResponseEntity<WorkInjuryDto.ApiResponse> preSettle(
            @RequestParam String visitNo,
            @Valid @RequestBody List<WorkInjuryDto.FeeDetail> feeDetails) {
        
        logger.info("收到费用预结算请求,就诊号: {}, 费用明细数量: {}", visitNo, feeDetails.size());
        WorkInjuryDto.ApiResponse response = workInjuryService.preSettle(visitNo, feeDetails);
        return ResponseEntity.ok(response);
    }

    /**
     * 正式结算
     */
    @PostMapping("/settle")
    public ResponseEntity<WorkInjuryDto.ApiResponse> settle(
            @RequestParam String visitNo,
            @RequestParam String preSettleId) {
        
        logger.info("收到正式结算请求,就诊号: {}, 预结算ID: {}", visitNo, preSettleId);
        WorkInjuryDto.ApiResponse response = workInjuryService.settle(visitNo, preSettleId);
        return ResponseEntity.ok(response);
    }

    /**
     * 完整就医流程
     */
    @PostMapping("/complete-process")
    public ResponseEntity<WorkInjuryDto.ApiResponse> completeProcess(
            @Valid @RequestBody Map<String, Object> request) {
        
        logger.info("收到完整就医流程请求");
        
        WorkInjuryDto.PatientRegisterRequest patientInfo = 
                convertToPatientRegisterRequest((Map<String, Object>) request.get("patientInfo"));
        
        @SuppressWarnings("unchecked")
        List<WorkInjuryDto.FeeDetail> feeDetails = 
                (List<WorkInjuryDto.FeeDetail>) request.get("feeDetails");
        
        WorkInjuryDto.ApiResponse response = workInjuryService.completeProcess(patientInfo, feeDetails);
        return ResponseEntity.ok(response);
    }

    /**
     * 批量上传处方明细
     */
    @PostMapping("/batch-upload")
    public ResponseEntity<WorkInjuryDto.ApiResponse> batchUploadPrescriptions(
            @RequestParam String patientId,
            @RequestParam String visitNo,
            @RequestBody List<Map<String, Object>> prescriptions) {
        
        logger.info("收到批量上传处方请求,患者: {}, 就诊号: {}, 处方数量: {}", 
                patientId, visitNo, prescriptions.size());
        
        WorkInjuryDto.ApiResponse response = workInjuryService.batchUploadPrescriptions(patientId, visitNo, prescriptions);
        return ResponseEntity.ok(response);
    }

    /**
     * 获取签到状态
     */
    @GetMapping("/signin-status")
    public ResponseEntity<WorkInjuryDto.ApiResponse> getSignInStatus() {
        WorkInjuryDto.ApiResponse response = workInjuryService.getSignInStatus();
        return ResponseEntity.ok(response);
    }

    /**
     * 智能重试交易
     */
    @PostMapping("/retry")
    public ResponseEntity<WorkInjuryDto.ApiResponse> retryTransaction(
            @RequestParam String transactionName,
            @RequestBody(required = false) Object businessParams,
            @RequestParam(required = false) Integer maxRetries,
            @RequestParam(required = false) Integer baseDelayMs) {
        
        logger.info("收到智能重试请求,交易: {}", transactionName);
        WorkInjuryDto.ApiResponse response = workInjuryService.retryTransaction(
                transactionName, businessParams, maxRetries, baseDelayMs);
        return ResponseEntity.ok(response);
    }

    /**
     * 交易冲正
     */
    @PostMapping("/reverse")
    public ResponseEntity<WorkInjuryDto.ApiResponse> reverseTransaction(
            @RequestParam String originalMessageId) {
        
        logger.info("收到交易冲正请求,原始报文ID: {}", originalMessageId);
        WorkInjuryDto.ApiResponse response = workInjuryService.reverseTransaction(originalMessageId);
        return ResponseEntity.ok(response);
    }

    /**
     * 工伤患者完整就医示例 - 组合接口
     */
    @PostMapping("/patient-visit")
    public ResponseEntity<Map<String, Object>> handlePatientVisit(
            @Valid @RequestBody WorkInjuryDto.PatientRegisterRequest registerRequest,
            @RequestParam(required = false) String identifyMode) {
        
        logger.info("收到工伤患者就医请求,患者编号: {}", registerRequest.getPsnNo());
        
        Map<String, Object> result = Map.of(
                "success", false,
                "message", "处理失败"
        );
        
        try {
            // 1. 读卡验证
            WorkInjuryDto.ApiResponse cardResult = workInjuryService.readCard(
                    identifyMode != null ? identifyMode : "1", null, null, null);
            
            if (!cardResult.isSuccess()) {
                return ResponseEntity.ok(Map.of(
                        "success", false,
                        "message", "读卡失败: " + cardResult.getMessage(),
                        "step", "读卡"
                ));
            }

            // 2. 患者登记
            WorkInjuryDto.ApiResponse registerResult = workInjuryService.registerPatient(registerRequest);
            
            if (!registerResult.isSuccess()) {
                return ResponseEntity.ok(Map.of(
                        "success", false,
                        "message", "患者登记失败: " + registerResult.getMessage(),
                        "step", "登记",
                        "cardInfo", cardResult.getData()
                ));
            }

            // 返回成功结果
            return ResponseEntity.ok(Map.of(
                    "success", true,
                    "message", "工伤患者就医处理成功",
                    "data", Map.of(
                            "cardInfo", cardResult.getData(),
                            "registerInfo", registerResult.getData()
                    )
            ));

        } catch (Exception e) {
            logger.error("工伤患者就医处理异常", e);
            return ResponseEntity.ok(Map.of(
                    "success", false,
                    "message", "系统异常: " + e.getMessage()
            ));
        }
    }

    /**
     * 转换为患者登记请求对象
     */
    private WorkInjuryDto.PatientRegisterRequest convertToPatientRegisterRequest(Map<String, Object> map) {
        WorkInjuryDto.PatientRegisterRequest request = new WorkInjuryDto.PatientRegisterRequest();
        request.setPsnNo((String) map.get("psn_no"));
        request.setVisitType((String) map.get("visit_type"));
        request.setDeptCode((String) map.get("dept_code"));
        request.setDeptName((String) map.get("dept_name"));
        request.setDoctorCode((String) map.get("doctor_code"));
        request.setDoctorName((String) map.get("doctor_name"));
        request.setDiagCode((String) map.get("diag_code"));
        request.setDiagName((String) map.get("diag_name"));
        request.setVisitTime((String) map.get("visit_time"));
        return request;
    }
}

主应用类

package com.hospital.workinjury;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * 江苏工伤联网结算集成应用
 */
@SpringBootApplication
public class WorkInjuryIntegrationApplication {

    public static void main(String[] args) {
        SpringApplication.run(WorkInjuryIntegrationApplication.class, args);
    }
}

使用示例

package com.hospital.workinjury.example;

import com.hospital.workinjury.dto.WorkInjuryDto;
import com.hospital.workinjury.service.WorkInjuryService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.List;

/**
 * 工伤联网业务使用示例
 */
@Component
public class WorkInjuryBusinessExample {

    private static final Logger logger = LoggerFactory.getLogger(WorkInjuryBusinessExample.class);
    private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    @Autowired
    private WorkInjuryService workInjuryService;

    /**
     * 工伤患者完整就医流程示例
     */
    public void demonstrateCompleteWorkInjuryProcess() {
        try {
            logger.info("=== 开始工伤患者完整就医流程演示 ===");

            // 1. 系统初始化
            logger.info("步骤1: 系统初始化");
            WorkInjuryDto.ApiResponse initResult = workInjuryService.initialize();
            if (!initResult.isSuccess()) {
                logger.error("系统初始化失败: {}", initResult.getMessage());
                return;
            }
            logger.info("系统初始化成功");

            // 2. 健康检查
            logger.info("步骤2: 系统健康检查");
            WorkInjuryDto.ApiResponse healthResult = workInjuryService.healthCheck();
            logger.info("健康检查结果: {}", healthResult.isSuccess() ? "正常" : healthResult.getMessage());

            // 3. 工伤患者读卡
            logger.info("步骤3: 工伤患者读卡");
            WorkInjuryDto.ApiResponse cardResult = workInjuryService.readCard("1", null, "001", "收费员张三");
            if (!cardResult.isSuccess()) {
                logger.error("读卡失败: {}", cardResult.getMessage());
                return;
            }
            logger.info("读卡成功,患者信息: {}", cardResult.getData());

            // 4. 患者登记
            logger.info("步骤4: 患者登记");
            WorkInjuryDto.PatientRegisterRequest registerRequest = createSampleRegisterRequest();
            WorkInjuryDto.ApiResponse registerResult = workInjuryService.registerPatient(registerRequest);
            if (!registerResult.isSuccess()) {
                logger.error("患者登记失败: {}", registerResult.getMessage());
                return;
            }
            logger.info("患者登记成功,登记信息: {}", registerResult.getData());

            // 从登记结果中获取就诊号
            String visitNo = extractVisitNo(registerResult.getData());
            if (visitNo == null) {
                logger.error("无法获取就诊号");
                return;
            }

            // 5. 费用预结算
            logger.info("步骤5: 费用预结算");
            List<WorkInjuryDto.FeeDetail> feeDetails = createSampleFeeDetails();
            WorkInjuryDto.ApiResponse preSettleResult = workInjuryService.preSettle(visitNo, feeDetails);
            if (!preSettleResult.isSuccess()) {
                logger.error("费用预结算失败: {}", preSettleResult.getMessage());
                return;
            }
            logger.info("费用预结算成功,预结算信息: {}", preSettleResult.getData());

            // 从预结算结果中获取预结算ID
            String preSettleId = extractPreSettleId(preSettleResult.getData());
            if (preSettleId == null) {
                logger.error("无法获取预结算ID");
                return;
            }

            // 6. 正式结算
            logger.info("步骤6: 正式结算");
            WorkInjuryDto.ApiResponse settleResult = workInjuryService.settle(visitNo, preSettleId);
            if (!settleResult.isSuccess()) {
                logger.error("正式结算失败: {}", settleResult.getMessage());
                return;
            }
            logger.info("正式结算成功,结算信息: {}", settleResult.getData());

            logger.info("=== 工伤患者完整就医流程演示成功 ===");

        } catch (Exception e) {
            logger.error("工伤患者就医流程演示异常", e);
        }
    }

    /**
     * 一键完成流程示例
     */
    public void demonstrateCompleteProcessAPI() {
        try {
            logger.info("=== 开始一键完成流程演示 ===");

            WorkInjuryDto.PatientRegisterRequest patientInfo = createSampleRegisterRequest();
            List<WorkInjuryDto.FeeDetail> feeDetails = createSampleFeeDetails();

            WorkInjuryDto.ApiResponse result = workInjuryService.completeProcess(patientInfo, feeDetails);
            
            if (result.isSuccess()) {
                logger.info("一键完成流程成功: {}", result.getData());
            } else {
                logger.error("一键完成流程失败: {}", result.getMessage());
            }

        } catch (Exception e) {
            logger.error("一键完成流程演示异常", e);
        }
    }

    /**
     * 创建示例患者登记请求
     */
    private WorkInjuryDto.PatientRegisterRequest createSampleRegisterRequest() {
        WorkInjuryDto.PatientRegisterRequest request = new WorkInjuryDto.PatientRegisterRequest();
        request.setPsnNo("32010219800101001X");  // 示例身份证号
        request.setVisitType("1");              // 门诊
        request.setDeptCode("001");             // 科室编码
        request.setDeptName("骨科");            // 科室名称
        request.setDoctorCode("DOC001");        // 医生编码
        request.setDoctorName("张主任");         // 医生姓名
        request.setDiagCode("M79.900");         // 诊断代码
        request.setDiagName("骨折");            // 诊断名称
        request.setVisitTime(LocalDateTime.now().format(DATE_TIME_FORMATTER));
        return request;
    }

    /**
     * 创建示例费用明细
     */
    private List<WorkInjuryDto.FeeDetail> createSampleFeeDetails() {
        String currentTime = LocalDateTime.now().format(DATE_TIME_FORMATTER);

        WorkInjuryDto.FeeDetail fee1 = new WorkInjuryDto.FeeDetail();
        fee1.setFeeOcurTime(currentTime);
        fee1.setMedListCodg("001");
        fee1.setMedListName("X光片检查");
        fee1.setMedChrgitmType("4");        // 医用材料
        fee1.setFeeAmt(new BigDecimal("120.00"));
        fee1.setCnt(1);
        fee1.setPric(new BigDecimal("120.00"));
        fee1.setSpec("胸部X光");
        fee1.setDeptCode("001");
        fee1.setDeptName("骨科");

        WorkInjuryDto.FeeDetail fee2 = new WorkInjuryDto.FeeDetail();
        fee2.setFeeOcurTime(currentTime);
        fee2.setMedListCodg("002");
        fee2.setMedListName("诊疗费");
        fee2.setMedChrgitmType("2");        // 诊疗项目
        fee2.setFeeAmt(new BigDecimal("15.00"));
        fee2.setCnt(1);
        fee2.setPric(new BigDecimal("15.00"));
        fee2.setSpec("门诊诊疗");
        fee2.setDeptCode("001");
        fee2.setDeptName("骨科");

        return Arrays.asList(fee1, fee2);
    }

    /**
     * 从登记结果中提取就诊号
     */
    private String extractVisitNo(Object data) {
        try {
            if (data instanceof Map) {
                @SuppressWarnings("unchecked")
                Map<String, Object> dataMap = (Map<String, Object>) data;
                return (String) dataMap.get("visit_no");
            }
        } catch (Exception e) {
            logger.error("提取就诊号失败", e);
        }
        return null;
    }

    /**
     * 从预结算结果中提取预结算ID
     */
    private String extractPreSettleId(Object data) {
        try {
            if (data instanceof Map) {
                @SuppressWarnings("unchecked")
                Map<String, Object> dataMap = (Map<String, Object>) data;
                return (String) dataMap.get("pre_settle_id");
            }
        } catch (Exception e) {
            logger.error("提取预结算ID失败", e);
        }
        return null;
    }
}

这个Spring Boot示例提供了:

  1. 完整的项目结构:包含配置、DTO、服务层、控制器层
  2. 生产级配置:包含重试机制、超时设置、日志配置
  3. RESTful API:标准的Spring Boot REST接口设计
  4. 数据验证:使用Bean Validation进行参数校验
  5. 异常处理:完善的异常处理和日志记录
  6. 业务示例:实际的工伤患者就医流程演示

通过这个Spring Boot项目,Java开发者可以快速集成江苏工伤联网结算功能到现有的医院信息系统中。


🔧 高级功能和最佳实践

💡 最佳实践建议

  1. 异常处理: 每个交易都应该有完善的错误处理
  2. 超时设置: 网络请求建议设置10-30秒超时
  3. 重试机制: 对于网络错误,建议实现指数退避重试
  4. 日志记录: 记录所有交易的请求和响应,便于排查问题
  5. 状态管理: 跟踪患者的就医流程状态

🔄 完整的HIS系统集成示例

// HIS系统工伤模块完整示例
class HISWorkInjuryModule {
    constructor() {
        this.api = new WorkInjuryAPI();
        this.isInitialized = false;
    }

    // 系统启动时初始化
    async initialize() {
        try {
            const config = {
                fixmedinsCode: this.getHospitalCode(),      // 从配置文件读取
                fixmedinsName: this.getHospitalName(),      // 从配置文件读取
                defaultOperator: this.getCurrentUserId(),   // 当前登录用户
                defaultOperatorName: this.getCurrentUserName()
            };

            const result = await this.api.init(config);
            if (result.success) {
                this.isInitialized = true;
                this.showMessage('工伤系统初始化成功', 'success');
            } else {
                throw new Error(result.message);
            }
        } catch (error) {
            this.showMessage('工伤系统初始化失败: ' + error.message, 'error');
            throw error;
        }
    }

    // 工伤患者就医主流程
    async processWorkInjuryPatient(deptCode, deptName, doctorCode, doctorName) {
        if (!this.isInitialized) {
            throw new Error('系统未初始化');
        }

        const patientSession = {
            step: 0,
            data: {}
        };

        try {
            // 步骤1: 读卡
            this.updateProgress('正在读取工伤职工信息...', 1);
            const cardResult = await this.api.readCard();
            
            if (!cardResult.success) {
                throw new Error('读卡失败: ' + cardResult.message);
            }

            patientSession.data.patient = cardResult.data;
            patientSession.step = 1;
            
            this.displayPatientInfo(cardResult.data);
            this.updateProgress('读卡成功', 1, true);

            // 步骤2: 登记
            this.updateProgress('正在进行就医登记...', 2);
            const registerData = {
                psn_no: cardResult.data.psn_no,
                visit_type: '1',
                dept_code: deptCode,
                dept_name: deptName,
                doctor_code: doctorCode,
                doctor_name: doctorName,
                visit_time: new Date().toISOString().slice(0, 19).replace('T', ' ')
            };

            const registerResult = await this.api.registerPatient(registerData);
            
            if (!registerResult.success) {
                throw new Error('登记失败: ' + registerResult.message);
            }

            patientSession.data.visitNo = registerResult.data.visit_no;
            patientSession.step = 2;
            
            this.updateProgress('登记成功,就诊号: ' + registerResult.data.visit_no, 2, true);

            // 返回患者会话,供后续费用处理使用
            return patientSession;

        } catch (error) {
            this.showMessage('工伤患者处理失败: ' + error.message, 'error');
            this.updateProgress('处理失败', patientSession.step, false);
            throw error;
        }
    }

    // 费用结算处理
    async processFeeSettlement(patientSession, feeItems) {
        try {
            // 转换费用明细格式
            const feeDetails = feeItems.map(item => ({
                fee_ocur_time: new Date().toISOString().slice(0, 19).replace('T', ' '),
                med_list_codg: item.code,
                med_list_name: item.name,
                med_chrgitm_type: item.type,
                fee_amt: item.amount,
                cnt: item.quantity,
                pric: item.price,
                spec: item.spec || '',
                dept_code: item.deptCode,
                dept_name: item.deptName
            }));

            // 步骤3: 预结算
            this.updateProgress('正在进行费用预结算...', 3);
            const preResult = await this.api.preSettle(patientSession.data.visitNo, feeDetails);
            
            if (!preResult.success) {
                throw new Error('预结算失败: ' + preResult.message);
            }

            // 显示预结算结果供用户确认
            const confirmed = await this.showSettlementConfirmation({
                totalAmount: preResult.data.total_amt,
                fundPay: preResult.data.fund_pay_amt,
                personalPay: preResult.data.psn_pay_amt,
                patientName: patientSession.data.patient.psn_name
            });

            if (!confirmed) {
                throw new Error('用户取消结算');
            }

            this.updateProgress('预结算成功,等待确认结算...', 3, true);

            // 步骤4: 正式结算
            this.updateProgress('正在进行正式结算...', 4);
            const settleResult = await this.api.settle(
                patientSession.data.visitNo, 
                preResult.data.pre_settle_id
            );
            
            if (!settleResult.success) {
                throw new Error('结算失败: ' + settleResult.message);
            }

            this.updateProgress('结算完成!', 4, true);

            // 打印结算凭证
            await this.printSettlementReceipt(settleResult.data);

            // 更新HIS系统数据
            await this.updateHISRecords(patientSession, settleResult.data);

            this.showMessage('工伤费用结算成功!', 'success');
            return settleResult.data;

        } catch (error) {
            this.showMessage('费用结算失败: ' + error.message, 'error');
            throw error;
        }
    }

    // 工具方法
    updateProgress(message, step, isComplete = false) {
        // 更新界面进度显示
        console.log(`步骤${step}: ${message}`);
    }

    displayPatientInfo(patientData) {
        // 在界面显示患者信息
        console.log('患者信息:', patientData);
    }

    async showSettlementConfirmation(settlementInfo) {
        // 显示结算确认对话框
        return confirm(`
            患者: ${settlementInfo.patientName}
            总费用: ¥${settlementInfo.totalAmount}
            基金支付: ¥${settlementInfo.fundPay}
            个人支付: ¥${settlementInfo.personalPay}
            
            确认进行结算吗?
        `);
    }

    async printSettlementReceipt(settlementData) {
        // 打印结算凭证
        console.log('打印结算凭证:', settlementData);
    }

    async updateHISRecords(patientSession, settlementData) {
        // 更新HIS系统相关记录
        console.log('更新HIS记录');
    }

    showMessage(message, type) {
        // 显示消息提示
        console.log(`[${type.toUpperCase()}] ${message}`);
    }

    // 配置获取方法
    getHospitalCode() { return 'SQ201348'; }
    getHospitalName() { return '沭阳铭和医院'; }
    getCurrentUserId() { return '001'; }
    getCurrentUserName() { return '收费员张三'; }
}

// 使用示例
const workInjuryModule = new HISWorkInjuryModule();

// 系统启动时
await workInjuryModule.initialize();

// 工伤患者就医时
const patientSession = await workInjuryModule.processWorkInjuryPatient(
    '001',        // 科室编码
    '骨科',       // 科室名称
    'DOC001',     // 医生编码
    '张主任'      // 医生姓名
);

// 收费结算时
const feeItems = [
    {
        code: '001',
        name: 'X光检查',
        type: '4',
        amount: 120.00,
        quantity: 1,
        price: 120.00,
        deptCode: '001',
        deptName: '骨科'
    }
];

await workInjuryModule.processFeeSettlement(patientSession, feeItems);

jQuery Ajax (传统方式)

对于使用传统jQuery技术栈的医院HIS系统,以下是经典的Ajax调用方式:

完整的工伤患者就医流程 (jQuery版本)

/**
 * 传统jQuery Ajax方式 - 适用于现有HIS系统
 * 使用回调函数处理异步操作
 */

// 全局配置
var WORKINJURY_API_URL = 'http://localhost:8321/api/entry/workinjury';

// 1. 系统初始化
function initWorkInjurySystem() {
    $.ajax({
        url: WORKINJURY_API_URL,
        type: 'POST',
        contentType: 'application/json',
        data: JSON.stringify({
            "action": "init",
            "config": {
                "fixmedinsCode": "SQ201348",
                "fixmedinsName": "沭阳铭和医院",
                "receiverSysCode": "JSYTH",
                "interfaceVersion": "V2.1",
                "operatorType": "1",
                "defaultOperator": "001",
                "defaultOperatorName": "收费员张三",
                "logPath": "logs/workinjury/"
            }
        }),
        success: function(result) {
            if (result.success) {
                console.log('工伤系统初始化成功');
                showMessage('工伤系统初始化成功', 'success');
            } else {
                console.error('初始化失败:', result.message);
                showMessage('初始化失败: ' + result.message, 'error');
            }
        },
        error: function(xhr, status, error) {
            console.error('请求失败:', error);
            showMessage('系统连接失败,请检查网络', 'error');
        }
    });
}

// 2. 工伤患者读卡
function readWorkInjuryCard() {
    showProgress('正在读取工伤职工信息...');
    
    $.ajax({
        url: WORKINJURY_API_URL,
        type: 'POST',
        contentType: 'application/json',
        data: JSON.stringify({
            "action": "transaction",
            "transactionName": "读卡",
            "identifyMode": "1",
            "operatorId": "001",
            "operatorName": "收费员张三"
        }),
        success: function(result) {
            hideProgress();
            if (result.success) {
                var cardData = result.data;
                console.log('读卡成功,患者:', cardData.psn_name);
                
                // 显示患者信息
                displayPatientInfo(cardData);
                showMessage('读卡成功,患者: ' + cardData.psn_name, 'success');
                
                // 启用登记功能
                enableRegistration(cardData);
            } else {
                console.error('读卡失败:', result.message);
                showMessage('读卡失败: ' + result.message, 'error');
            }
        },
        error: function(xhr, status, error) {
            hideProgress();
            console.error('读卡请求失败:', error);
            showMessage('读卡请求失败,请重试', 'error');
        }
    });
}

// 3. 患者登记
function registerWorkInjuryPatient(patientData) {
    showProgress('正在进行患者登记...');
    
    var registerParams = {
        "psn_no": patientData.psn_no,
        "visit_type": "1",
        "dept_code": $("#deptCode").val(),
        "dept_name": $("#deptName").val(),
        "doctor_code": $("#doctorCode").val(),
        "doctor_name": $("#doctorName").val(),
        "diag_code": $("#diagCode").val(),
        "diag_name": $("#diagName").val(),
        "visit_time": new Date().toISOString().slice(0, 19).replace('T', ' ')
    };
    
    $.ajax({
        url: WORKINJURY_API_URL,
        type: 'POST',
        contentType: 'application/json',
        data: JSON.stringify({
            "action": "transaction",
            "transactionName": "RegisterPatient",
            "businessParams": registerParams,
            "identifyMode": "1"
        }),
        success: function(result) {
            hideProgress();
            if (result.success) {
                var registerData = result.data;
                console.log('登记成功,就诊号:', registerData.visit_no);
                
                // 保存就诊号到全局变量
                window.currentVisitNo = registerData.visit_no;
                
                showMessage('登记成功,就诊号: ' + registerData.visit_no, 'success');
                
                // 启用费用录入功能
                enableFeeEntry();
            } else {
                console.error('登记失败:', result.message);
                showMessage('登记失败: ' + result.message, 'error');
            }
        },
        error: function(xhr, status, error) {
            hideProgress();
            console.error('登记请求失败:', error);
            showMessage('登记请求失败,请重试', 'error');
        }
    });
}

// 4. 工伤费用结算(完整流程)
function settleWorkInjury(visitNo, feeDetails) {
    showProgress('正在进行费用预结算...');
    
    // 先预结算
    $.ajax({
        url: WORKINJURY_API_URL,
        type: 'POST',
        contentType: 'application/json',
        data: JSON.stringify({
            "action": "transaction",
            "transactionName": "PreSettle",
            "businessParams": {
                "visit_no": visitNo,
                "fee_details": feeDetails
            }
        }),
        success: function(preResult) {
            if (preResult.success) {
                var preSettleData = preResult.data;
                console.log('预结算成功');
                
                // 显示预结算结果供用户确认
                var confirmMessage = 
                    '预结算成功!\n' +
                    '总费用: ¥' + preSettleData.total_amt + '\n' +
                    '基金支付: ¥' + preSettleData.fund_pay_amt + '\n' +
                    '个人支付: ¥' + preSettleData.psn_pay_amt + '\n\n' +
                    '确认进行正式结算吗?';
                
                if (confirm(confirmMessage)) {
                    // 用户确认后进行正式结算
                    updateProgress('正在进行正式结算...');
                    
                    $.ajax({
                        url: WORKINJURY_API_URL,
                        type: 'POST',
                        contentType: 'application/json',
                        data: JSON.stringify({
                            "action": "transaction",
                            "transactionName": "Settle",
                            "businessParams": {
                                "visit_no": visitNo,
                                "pre_settle_id": preSettleData.pre_settle_id
                            }
                        }),
                        success: function(settleResult) {
                            hideProgress();
                            if (settleResult.success) {
                                var settlementData = settleResult.data;
                                console.log('结算成功,发票号:', settlementData.invoice_no);
                                
                                // 显示结算成功信息
                                var successMessage = 
                                    '工伤费用结算成功!\n' +
                                    '结算流水号: ' + settlementData.settle_id + '\n' +
                                    '发票号: ' + settlementData.invoice_no + '\n' +
                                    '总费用: ¥' + settlementData.total_amt + '\n' +
                                    '基金支付: ¥' + settlementData.fund_pay_amt + '\n' +
                                    '个人支付: ¥' + settlementData.psn_pay_amt;
                                
                                showMessage(successMessage, 'success');
                                
                                // 处理结算成功后的业务逻辑
                                handleSettlementSuccess(settlementData);
                                
                                // 打印凭证
                                printSettlementReceipt(settlementData);
                                
                            } else {
                                console.error('结算失败:', settleResult.message);
                                showMessage('结算失败: ' + settleResult.message, 'error');
                            }
                        },
                        error: function(xhr, status, error) {
                            hideProgress();
                            console.error('结算请求失败:', error);
                            showMessage('结算请求失败,请重试', 'error');
                        }
                    });
                } else {
                    hideProgress();
                    showMessage('用户取消结算', 'info');
                }
            } else {
                hideProgress();
                console.error('预结算失败:', preResult.message);
                showMessage('预结算失败: ' + preResult.message, 'error');
            }
        },
        error: function(xhr, status, error) {
            hideProgress();
            console.error('预结算请求失败:', error);
            showMessage('预结算请求失败,请重试', 'error');
        }
    });
}

// 5. 批量上传处方明细
function uploadPrescriptions(patientId, visitNo, prescriptions) {
    showProgress('正在上传处方明细...');
    
    $.ajax({
        url: WORKINJURY_API_URL,
        type: 'POST',
        contentType: 'application/json',
        data: JSON.stringify({
            "action": "batch_upload",
            "patientId": patientId,
            "visitNo": visitNo,
            "prescriptions": prescriptions
        }),
        success: function(result) {
            hideProgress();
            if (result.success) {
                console.log('处方上传成功');
                showMessage('处方明细上传成功', 'success');
            } else {
                console.error('处方上传失败:', result.message);
                showMessage('处方上传失败: ' + result.message, 'error');
            }
        },
        error: function(xhr, status, error) {
            hideProgress();
            console.error('处方上传请求失败:', error);
            showMessage('处方上传请求失败,请重试', 'error');
        }
    });
}

// 6. 系统健康检查
function checkSystemHealth() {
    $.ajax({
        url: WORKINJURY_API_URL,
        type: 'POST',
        contentType: 'application/json',
        data: JSON.stringify({
            "action": "health"
        }),
        success: function(result) {
            if (result.success && result.data.overallHealth) {
                showMessage('系统状态正常', 'success');
                updateSystemStatus('正常', 'green');
            } else {
                showMessage('系统存在问题,请检查', 'warning');
                updateSystemStatus('异常', 'red');
            }
        },
        error: function(xhr, status, error) {
            showMessage('系统检查失败', 'error');
            updateSystemStatus('离线', 'gray');
        }
    });
}

// 7. 一键完成就医流程
function completeWorkInjuryProcess() {
    showProgress('正在执行完整就医流程...');
    
    var patientInfo = {
        "visit_type": "1",
        "dept_code": $("#deptCode").val(),
        "dept_name": $("#deptName").val(),
        "doctor_code": $("#doctorCode").val(),
        "doctor_name": $("#doctorName").val()
    };
    
    var feeDetails = getFeeDetailsFromForm(); // 从表单获取费用明细
    
    $.ajax({
        url: WORKINJURY_API_URL,
        type: 'POST',
        contentType: 'application/json',
        data: JSON.stringify({
            "action": "complete_process",
            "patientInfo": patientInfo,
            "feeDetails": feeDetails
        }),
        success: function(result) {
            hideProgress();
            if (result.success) {
                var processSteps = result.processSteps;
                var successSteps = processSteps.filter(function(step) {
                    return step.success;
                });
                
                console.log('完整流程执行成功,完成步骤:', successSteps.length);
                showMessage('工伤就医流程全部完成!', 'success');
                
                // 显示流程结果
                displayProcessResults(processSteps);
            } else {
                console.error('完整流程失败:', result.message);
                showMessage('完整流程失败: ' + result.message, 'error');
            }
        },
        error: function(xhr, status, error) {
            hideProgress();
            console.error('完整流程请求失败:', error);
            showMessage('完整流程请求失败,请重试', 'error');
        }
    });
}

// 辅助函数

// 显示消息提示
function showMessage(message, type) {
    var className = 'alert-' + type;
    var alertHtml = '<div class="alert ' + className + ' alert-dismissible fade show" role="alert">' +
                   message +
                   '<button type="button" class="close" data-dismiss="alert">' +
                   '<span>&times;</span></button></div>';
    
    $('#messageContainer').html(alertHtml);
    
    // 3秒后自动隐藏
    setTimeout(function() {
        $('.alert').alert('close');
    }, 3000);
}

// 显示进度提示
function showProgress(message) {
    $('#progressMessage').text(message);
    $('#progressModal').modal('show');
}

// 更新进度消息
function updateProgress(message) {
    $('#progressMessage').text(message);
}

// 隐藏进度提示
function hideProgress() {
    $('#progressModal').modal('hide');
}

// 显示患者信息
function displayPatientInfo(patientData) {
    $('#patientName').text(patientData.psn_name);
    $('#patientGender').text(patientData.gend === '1' ? '男' : '女');
    $('#patientBirthday').text(formatDate(patientData.brdy));
    $('#patientCardNo').text(patientData.card_sn);
    $('#patientCompany').text(patientData.emp_name);
    
    $('#patientInfoPanel').show();
}

// 启用登记功能
function enableRegistration(cardData) {
    $('#registrationSection').show();
    $('#btnRegister').prop('disabled', false);
    
    // 自动填充患者编号
    $('#patientPsnNo').val(cardData.psn_no);
}

// 启用费用录入功能
function enableFeeEntry() {
    $('#feeEntrySection').show();
    $('#btnAddFee').prop('disabled', false);
    $('#btnSettle').prop('disabled', false);
}

// 从表单获取费用明细
function getFeeDetailsFromForm() {
    var feeDetails = [];
    $('#feeTable tbody tr').each(function() {
        var row = $(this);
        var feeDetail = {
            "fee_ocur_time": new Date().toISOString().slice(0, 19).replace('T', ' '),
            "med_list_codg": row.find('.fee-code').val(),
            "med_list_name": row.find('.fee-name').val(),
            "med_chrgitm_type": row.find('.fee-type').val(),
            "fee_amt": parseFloat(row.find('.fee-amount').val()),
            "cnt": parseInt(row.find('.fee-count').val()),
            "pric": parseFloat(row.find('.fee-price').val()),
            "spec": row.find('.fee-spec').val(),
            "dept_code": $('#deptCode').val(),
            "dept_name": $('#deptName').val()
        };
        feeDetails.push(feeDetail);
    });
    return feeDetails;
}

// 处理结算成功
function handleSettlementSuccess(settlementData) {
    // 保存结算信息到本地存储
    localStorage.setItem('lastSettlement', JSON.stringify(settlementData));
    
    // 更新界面状态
    $('#btnSettle').prop('disabled', true).text('已结算');
    $('#settlementResult').show();
    
    // 可以在这里添加更多业务逻辑
    // 比如:更新数据库、发送通知等
}

// 打印结算凭证
function printSettlementReceipt(settlementData) {
    // 构造打印内容
    var printContent = 
        '===============================\n' +
        '      工伤保险结算凭证\n' +
        '===============================\n' +
        '结算时间: ' + settlementData.settle_time + '\n' +
        '结算流水号: ' + settlementData.settle_id + '\n' +
        '发票号: ' + settlementData.invoice_no + '\n' +
        '-------------------------------\n' +
        '总费用: ¥' + settlementData.total_amt + '\n' +
        '基金支付: ¥' + settlementData.fund_pay_amt + '\n' +
        '个人支付: ¥' + settlementData.psn_pay_amt + '\n' +
        '===============================';
    
    // 在这里调用实际的打印方法
    console.log('打印凭证:\n' + printContent);
    
    // 如果有打印机接口,可以这样调用:
    // window.print();
    // 或者调用特定的打印组件
}

// 更新系统状态显示
function updateSystemStatus(status, color) {
    $('#systemStatus').text(status).css('color', color);
}

// 格式化日期
function formatDate(dateStr) {
    if (!dateStr || dateStr.length < 8) return dateStr;
    return dateStr.substring(0, 4) + '-' + 
           dateStr.substring(4, 6) + '-' + 
           dateStr.substring(6, 8);
}

// 页面加载完成后的初始化
$(document).ready(function() {
    // 系统启动时自动初始化工伤系统
    initWorkInjurySystem();
    
    // 定期检查系统健康状态(每5分钟)
    setInterval(checkSystemHealth, 5 * 60 * 1000);
    
    // 绑定按钮事件
    $('#btnReadCard').click(readWorkInjuryCard);
    $('#btnRegister').click(function() {
        var patientData = { psn_no: $('#patientPsnNo').val() };
        registerWorkInjuryPatient(patientData);
    });
    $('#btnSettle').click(function() {
        var visitNo = window.currentVisitNo;
        var feeDetails = getFeeDetailsFromForm();
        settleWorkInjury(visitNo, feeDetails);
    });
    $('#btnCompleteProcess').click(completeWorkInjuryProcess);
    
    console.log('工伤联网系统界面初始化完成');
});

HTML界面示例

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>工伤联网结算系统</title>
    <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/4.6.0/css/bootstrap.min.css" rel="stylesheet">
    <style>
        .patient-info { border: 1px solid #ddd; padding: 15px; margin: 10px 0; border-radius: 5px; }
        .system-status { position: fixed; top: 10px; right: 10px; font-weight: bold; }
        .hidden { display: none; }
    </style>
</head>
<body>
    <div class="container mt-4">
        <h2>江苏工伤联网结算系统</h2>
        
        <!-- 系统状态 -->
        <div class="system-status">
            系统状态: <span id="systemStatus">检查中...</span>
        </div>
        
        <!-- 消息提示区域 -->
        <div id="messageContainer"></div>
        
        <!-- 患者信息区域 -->
        <div class="card">
            <div class="card-header">
                <h5>工伤患者信息</h5>
            </div>
            <div class="card-body">
                <button id="btnReadCard" class="btn btn-primary">读卡</button>
                
                <div id="patientInfoPanel" class="patient-info hidden">
                    <div class="row">
                        <div class="col-md-3">姓名: <span id="patientName"></span></div>
                        <div class="col-md-3">性别: <span id="patientGender"></span></div>
                        <div class="col-md-3">出生日期: <span id="patientBirthday"></span></div>
                        <div class="col-md-3">社保卡号: <span id="patientCardNo"></span></div>
                    </div>
                    <div class="row mt-2">
                        <div class="col-md-6">单位名称: <span id="patientCompany"></span></div>
                    </div>
                </div>
            </div>
        </div>
        
        <!-- 患者登记区域 -->
        <div id="registrationSection" class="card mt-3 hidden">
            <div class="card-header">
                <h5>患者登记</h5>
            </div>
            <div class="card-body">
                <div class="row">
                    <div class="col-md-3">
                        <label>患者编号:</label>
                        <input type="text" id="patientPsnNo" class="form-control" readonly>
                    </div>
                    <div class="col-md-3">
                        <label>科室编码:</label>
                        <input type="text" id="deptCode" class="form-control" value="001">
                    </div>
                    <div class="col-md-3">
                        <label>科室名称:</label>
                        <input type="text" id="deptName" class="form-control" value="骨科">
                    </div>
                    <div class="col-md-3">
                        <label>医生编码:</label>
                        <input type="text" id="doctorCode" class="form-control" value="DOC001">
                    </div>
                </div>
                <div class="row mt-2">
                    <div class="col-md-3">
                        <label>医生姓名:</label>
                        <input type="text" id="doctorName" class="form-control" value="张主任">
                    </div>
                    <div class="col-md-3">
                        <label>诊断代码:</label>
                        <input type="text" id="diagCode" class="form-control" value="M79.900">
                    </div>
                    <div class="col-md-3">
                        <label>诊断名称:</label>
                        <input type="text" id="diagName" class="form-control" value="骨折">
                    </div>
                    <div class="col-md-3 d-flex align-items-end">
                        <button id="btnRegister" class="btn btn-success" disabled>登记</button>
                    </div>
                </div>
            </div>
        </div>
        
        <!-- 费用录入区域 -->
        <div id="feeEntrySection" class="card mt-3 hidden">
            <div class="card-header">
                <h5>费用录入</h5>
            </div>
            <div class="card-body">
                <table id="feeTable" class="table table-bordered">
                    <thead>
                        <tr>
                            <th>项目编码</th>
                            <th>项目名称</th>
                            <th>项目类别</th>
                            <th>数量</th>
                            <th>单价</th>
                            <th>金额</th>
                            <th>规格</th>
                        </tr>
                    </thead>
                    <tbody>
                        <tr>
                            <td><input type="text" class="form-control fee-code" value="001"></td>
                            <td><input type="text" class="form-control fee-name" value="X光片检查"></td>
                            <td>
                                <select class="form-control fee-type">
                                    <option value="1">药品</option>
                                    <option value="2">诊疗</option>
                                    <option value="3">服务</option>
                                    <option value="4" selected>检查</option>
                                </select>
                            </td>
                            <td><input type="number" class="form-control fee-count" value="1"></td>
                            <td><input type="number" class="form-control fee-price" step="0.01" value="120.00"></td>
                            <td><input type="number" class="form-control fee-amount" step="0.01" value="120.00" readonly></td>
                            <td><input type="text" class="form-control fee-spec" value="胸部X光"></td>
                        </tr>
                    </tbody>
                </table>
                <div class="mt-3">
                    <button id="btnAddFee" class="btn btn-info" disabled>添加费用</button>
                    <button id="btnSettle" class="btn btn-warning ml-2" disabled>费用结算</button>
                    <button id="btnCompleteProcess" class="btn btn-success ml-2">一键完成流程</button>
                </div>
            </div>
        </div>
        
        <!-- 结算结果区域 -->
        <div id="settlementResult" class="card mt-3 hidden">
            <div class="card-header">
                <h5>结算结果</h5>
            </div>
            <div class="card-body">
                <div class="alert alert-success">
                    结算完成!请查看结算凭证。
                </div>
            </div>
        </div>
    </div>
    
    <!-- 进度提示模态框 -->
    <div class="modal fade" id="progressModal" tabindex="-1" role="dialog" data-backdrop="static" data-keyboard="false">
        <div class="modal-dialog modal-dialog-centered" role="document">
            <div class="modal-content">
                <div class="modal-body text-center">
                    <div class="spinner-border text-primary" role="status">
                        <span class="sr-only">Loading...</span>
                    </div>
                    <div class="mt-3">
                        <span id="progressMessage">正在处理...</span>
                    </div>
                </div>
            </div>
        </div>
    </div>

    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/4.6.0/js/bootstrap.bundle.min.js"></script>
    <script>
        // 在这里引入上面的工伤联网JavaScript代码
    </script>
</body>
</html>

这个jQuery Ajax版本特别适合:

传统HIS系统:使用jQuery技术栈的现有系统
快速集成:最小化代码修改,易于嵌入
兼容性好:支持IE8+等老版本浏览器
可视化界面:提供完整的操作界面示例
错误处理:完善的用户友好提示

通过这个示例,使用传统技术栈的医院可以快速实现工伤联网结算功能的集成。


📞 技术支持和联系方式

🆘 遇到问题怎么办?

  1. 检查错误码: 参考本文档的错误处理部分
  2. 查看日志: 检查 logs/workinjury/ 目录下的日志文件
  3. 系统健康检查: 调用 health 接口检查系统状态
  4. 重启服务: 重启ThCardReader服务程序

📋 提交问题时请提供:

  1. 完整的错误响应信息
  2. 调用的接口和参数
  3. 日志文件内容
  4. 系统环境信息

🔄 接口版本更新

当前接口版本:V2.1

建议定期检查是否有新版本的接口规范。


📝 总结

江苏工伤联网结算HTTP接口为医院HIS系统提供了完整的工伤联网结算能力:

功能完整: 覆盖读卡、登记、结算等全流程
简单易用: 统一的HTTP POST接口,JSON格式交互
错误处理: 完善的错误码体系和错误信息
生产就绪: 支持重试、监控、日志等企业级功能
跨平台: 支持各种编程语言和开发框架

通过本接口,医院可以快速实现与江苏工伤联网平台的对接,为工伤职工提供便民的就医结算服务。