apply_step1接口说明.md 21 KB

apply/step1 接口说明

接口概述

接口路径: POST apply/step1

功能: 获取远程申请第一步的数据,用于初始化远程诊断申请表单

业务场景:

  • 下级医院医生发起远程诊断申请时调用
  • 如果是新建申请,会自动创建一条 remote_application 记录
  • 如果是编辑已有申请,直接返回申请数据

调用链路

Route (route.php)
  ↓
ApplyController::step1()
  ↓
ApplicationService::get_step1_data($params, $token)
  ↓
ApplicationDao (wrapper层)
  ↓
ApplicationModel (SQL查询)

请求参数

必传参数

参数名 类型 说明
id string 检查ID (exam_id)

可选参数

参数名 类型 说明
ra_id string 远程申请ID (remote_application表的id),编辑时传入

参数说明:

  • 新建申请: 只传 id(检查ID),系统自动创建 remote_application 记录
  • 编辑申请: 传 id + ra_id,系统返回已有的申请数据

核心业务逻辑

1. Controller 层

文件: controller/apply/ApplyController.php

public function step1(ApplicationService $service)
{
    try {
        // 1. 获取请求参数
        $params = $this->getParams();

        // 2. 参数验证(验证id是否存在)
        ApplicationValidate::checkId($params);

        // 3. 获取step1数据
        $info = $service->get_step1_data($params, $this->getToken());

        // 4. 返回结果
        return $this->success($info);
    } catch(\Exception $e) {
        $this->throwError($e->getMessage(), 0001);
    }
}

2. Service 层

文件: servies/application/ApplicationService.php

public function get_step1_data($params, $token)
{
    $id = $params['id'];  // exam_id

    // 情况1:编辑已有申请(传了ra_id)
    if (isset($params['ra_id']) && !empty($params['ra_id'])) {
        // 直接从 remote_application 表查询已有数据
        $info = $this->application->getApplication($params['ra_id']);
        return $info;
    }

    // 情况2:新建申请(没传ra_id)
    else {
        // 1. 定义需要查询的字段
        $field = [
            // exams表字段
            'e.id',
            'e.patient_num',           // 病历号
            'e.exam_datetime',         // 检查时间
            'e.exam_status',           // 检查状态
            'e.patient_id',            // 患者ID
            'e.study_id',              // 检查UID
            'e.exam_class',            // 检查类型
            'e.accession_num',         // 检查号
            'e.exam_project',          // 检查项目
            'e.name',                  // 姓名
            'e.age',                   // 年龄
            'e.sex',                   // 性别
            'e.phone',                 // 手机号
            'e.birthday',              // 生日
            'e.card_num',              // 身份证号
            'e.body_part',             // 检查部位
            'e.body_part_text',        // 检查部位文本
            'e.clin_symp',             // 临床症状
            'e.clin_diag',             // 临床诊断

            // register表字段
            'r.exam_sub_class',        // 检查子类型
            'r.device_name as device', // 设备名称
            'r.illness_desc',          // 病情描述(患者主诉)
            'r.phys_sign',             // 体征
            'r.anamnesis',             // 既往史
            'r.family_ill',            // 家族史
            'r.application_department as remote_department', // 开单科室
            'r.ext as remark'          // 备注
        ];

        // 2. 查询检查数据(联查 exams + register)
        $info = $this->application->getStep1Data($id, $field);

        // 3. 获取当前用户信息
        $user = $this->getUser($token);

        // 4. 获取用户所在机构名称
        $ins_name = $this->application->getInstitutionName($user['institution_id']);

        // 5. 构造 remote_application 初始数据
        $data = [
            'id' => UUIDUtils::uuid(),              // 申请ID(UUID)
            'local_institution_id' => $user['institution_id'],
            'local_institution_name' => $ins_name,
            'req_date_time' => date('Y-m-d H:i:s', time()),
            'name' => $info['name'],
            'age' => $info['age'],
            'sex' => $info['sex'],
            'patient_num' => $info['patient_num'],
            'accession_num' => $info['accession_num'],
            'exam_id' => $info['id'],
            'report_status' => 1,                   // 初始状态:1
            'exam_class' => $info['exam_class'],
            'phone' => $info['phone'],
            'study_id' => $info['study_id']
        ];

        // 6. 插入 remote_application 表(创建申请记录)
        $this->application->insertAplication($data);

        // 7. 将申请ID添加到返回数据中
        $info['application_id'] = $data['id'];

        return $info;
    }
}

3. Model 层 - SQL 查询

3.1 查询检查数据(新建申请)

文件: model/application/ApplicationModel.php

public function getStep1Data($id, $field)
{
    // 联查 exams 和 register 表
    $info = ExamModel::alias('e')
        ->join(['register' => 'r'], 'r.exam_id = e.id', 'LEFT')
        ->where('e.id', $id)
        ->field($field)
        ->find();
    return $info;
}

等价SQL:

SELECT
    e.id,
    e.patient_num,
    e.exam_datetime,
    e.exam_status,
    e.patient_id,
    e.study_id,
    e.exam_class,
    e.accession_num,
    e.exam_project,
    e.name,
    e.age,
    e.sex,
    e.phone,
    e.birthday,
    e.card_num,
    e.body_part,
    e.body_part_text,
    e.clin_symp,
    e.clin_diag,
    r.exam_sub_class,
    r.device_name as device,
    r.illness_desc,
    r.phys_sign,
    r.anamnesis,
    r.family_ill,
    r.application_department as remote_department,
    r.ext as remark
FROM exams e
LEFT JOIN register r ON r.exam_id = e.id
WHERE e.id = :exam_id

涉及表:

  • exams - 检查表
  • register - 登记表

3.2 查询已有申请(编辑)

public function getApplication($id)
{
    $info = $this->where('id', $id)->find();
    $info['application_id'] = $id;
    return $info;
}

等价SQL:

SELECT *
FROM remote_application
WHERE id = :ra_id

涉及表:

  • remote_application - 远程申请表

3.3 插入申请记录

public function insertAplication($info)
{
    $data = $this->insert($info);
    return $data;
}

等价SQL:

INSERT INTO remote_application (
    id,
    local_institution_id,
    local_institution_name,
    req_date_time,
    name,
    age,
    sex,
    patient_num,
    accession_num,
    exam_id,
    report_status,
    exam_class,
    phone,
    study_id
) VALUES (...)

3.4 查询机构名称

public function getInstitutionName($id)
{
    $name = InstitutionModel::where('id', $id)->value('name');
    return $name;
}

等价SQL:

SELECT name
FROM institution
WHERE id = :institution_id

数据库表结构

1. exams (检查表)

主要字段:

  • id - 检查ID(主键)
  • patient_id - 患者ID
  • patient_num - 病历号
  • exam_datetime - 检查时间
  • exam_status - 检查状态
  • study_id - 检查UID
  • exam_class - 检查类型
  • accession_num - 检查号
  • exam_project - 检查项目
  • name - 姓名
  • age - 年龄
  • sex - 性别
  • phone - 手机号
  • birthday - 生日
  • card_num - 身份证号
  • body_part - 检查部位
  • body_part_text - 检查部位文本
  • clin_symp - 临床症状
  • clin_diag - 临床诊断

2. register (登记表)

主要字段:

  • id - 登记ID(主键)
  • exam_id - 检查ID(关联exams.id)
  • exam_sub_class - 检查子类型
  • device_name - 设备名称
  • illness_desc - 病情描述(患者主诉)
  • phys_sign - 体征
  • anamnesis - 既往史
  • family_ill - 家族史
  • application_department - 开单科室
  • ext - 备注

3. remote_application (远程申请表)

主要字段:

  • id - 申请ID(主键,UUID)
  • exam_id - 本地检查ID
  • local_institution_id - 下级机构ID
  • local_institution_name - 下级机构名称
  • super_institution_id - 上级机构ID(后续步骤填写)
  • super_institution_name - 上级机构名称(后续步骤填写)
  • req_date_time - 申请时间
  • report_status - 申请状态(1=初始,2=待支付,3=已支付...)
  • name - 患者姓名
  • age - 年龄
  • sex - 性别
  • phone - 手机号
  • patient_num - 病历号
  • accession_num - 检查号
  • exam_class - 检查类型
  • exam_project - 检查项目
  • exam_sub_class - 检查子类型
  • body_part - 检查部位
  • body_part_text - 检查部位文本
  • birthday - 生日
  • card_num - 身份证号
  • study_id - 检查UID
  • illness_desc - 病情描述
  • phys_sign - 体征
  • anamnesis - 既往史
  • family_ill - 家族史
  • device_name - 设备名称

返回数据结构

新建申请返回数据示例

{
    "code": 200,
    "message": "成功",
    "data": {
        "id": "exam123456",
        "application_id": "uuid-xxxx-xxxx-xxxx",
        "patient_num": "P202512180001",
        "exam_datetime": "2025-12-18 10:30:00",
        "exam_status": 3,
        "patient_id": "patient123",
        "study_id": "1.2.840.113619...",
        "exam_class": "CT",
        "accession_num": "ACC20251218001",
        "exam_project": "胸部CT平扫",
        "name": "张三",
        "age": "45",
        "sex": "M",
        "phone": "13800138000",
        "birthday": "1979-05-20",
        "card_num": "110101197905201234",
        "body_part": "胸部",
        "body_part_text": "胸部",
        "clin_symp": "咳嗽、胸闷",
        "clin_diag": "肺部感染?",
        "exam_sub_class": "平扫",
        "device": "西门子CT-01",
        "illness_desc": "患者诉咳嗽2周,伴胸闷气短",
        "phys_sign": "体温37.5℃,呼吸稍急促",
        "anamnesis": "高血压病史5年",
        "family_ill": "父亲有糖尿病",
        "remote_department": "呼吸内科",
        "remark": ""
    }
}

编辑申请返回数据示例

{
    "code": 200,
    "message": "成功",
    "data": {
        "id": "uuid-xxxx-xxxx-xxxx",
        "application_id": "uuid-xxxx-xxxx-xxxx",
        "exam_id": "exam123456",
        "local_institution_id": "13000003",
        "local_institution_name": "某某医院",
        "super_institution_id": "13000001",
        "super_institution_name": "上级医院",
        "req_date_time": "2025-12-18 10:30:00",
        "report_status": 2,
        "name": "张三",
        "age": "45",
        "sex": "M",
        "phone": "13800138000",
        "patient_num": "P202512180001",
        "accession_num": "ACC20251218001",
        "exam_class": "CT",
        "exam_project": "胸部CT平扫",
        "exam_sub_class": "平扫",
        "body_part": "胸部",
        "body_part_text": "胸部",
        "birthday": "1979-05-20",
        "card_num": "110101197905201234",
        "study_id": "1.2.840.113619...",
        "illness_desc": "患者诉咳嗽2周,伴胸闷气短",
        "phys_sign": "体温37.5℃,呼吸稍急促",
        "anamnesis": "高血压病史5年",
        "family_ill": "父亲有糖尿病"
    }
}

业务规则总结

1. 申请状态 (report_status)

状态值 状态名称 说明
1 初始状态 step1创建申请时的初始状态
2 待支付 step2保存后,等待支付
3 已支付 支付完成,等待上级医生接诊
... ... 后续状态根据业务流程定义

2. 新建 vs 编辑

新建申请:

  • 只传 id(检查ID)
  • 系统自动:
    1. 从 exams + register 表查询检查数据
    2. 创建 remote_application 记录(status=1)
    3. 返回检查数据 + application_id

编辑申请:

  • id + ra_id
  • 直接从 remote_application 表返回已有数据

3. 数据来源

字段类型 数据来源
患者基本信息 exams表
检查信息 exams表 + register表
申请信息 自动生成(机构、时间、状态)
病情描述 register表

Java 实现建议

1. DTO

ApplyStep1RequestVO.java

@Data
@Schema(description = "远程申请Step1请求参数")
public class ApplyStep1RequestVO {

    @NotBlank(message = "检查ID不能为空")
    @Schema(description = "检查ID", required = true)
    private String id;

    @Schema(description = "远程申请ID(编辑时传入)")
    private String raId;
}

ApplyStep1ResponseDTO.java

@Data
@Schema(description = "远程申请Step1返回数据")
public class ApplyStep1ResponseDTO {

    @Schema(description = "检查ID")
    private String id;

    @Schema(description = "申请ID")
    private String applicationId;

    @Schema(description = "病历号")
    private String patientNum;

    @Schema(description = "检查时间")
    private String examDatetime;

    @Schema(description = "检查状态")
    private Integer examStatus;

    @Schema(description = "患者ID")
    private String patientId;

    @Schema(description = "检查UID")
    private String studyId;

    @Schema(description = "检查类型")
    private String examClass;

    @Schema(description = "检查号")
    private String accessionNum;

    @Schema(description = "检查项目")
    private String examProject;

    @Schema(description = "姓名")
    private String name;

    @Schema(description = "年龄")
    private String age;

    @Schema(description = "性别")
    private String sex;

    @Schema(description = "手机号")
    private String phone;

    @Schema(description = "生日")
    private String birthday;

    @Schema(description = "身份证号")
    private String cardNum;

    @Schema(description = "检查部位")
    private String bodyPart;

    @Schema(description = "检查部位文本")
    private String bodyPartText;

    @Schema(description = "临床症状")
    private String clinSymp;

    @Schema(description = "临床诊断")
    private String clinDiag;

    @Schema(description = "检查子类型")
    private String examSubClass;

    @Schema(description = "设备名称")
    private String device;

    @Schema(description = "病情描述(患者主诉)")
    private String illnessDesc;

    @Schema(description = "体征")
    private String physSign;

    @Schema(description = "既往史")
    private String anamnesis;

    @Schema(description = "家族史")
    private String familyIll;

    @Schema(description = "开单科室")
    private String remoteDepartment;

    @Schema(description = "备注")
    private String remark;
}

2. Mapper

RemoteApplicationMapper.java

@Mapper
public interface RemoteApplicationMapper extends BaseMapper<RemoteApplication> {

    /**
     * 查询Step1数据(联查exams和register)
     */
    ApplyStep1ResponseDTO getStep1Data(@Param("examId") String examId);
}

RemoteApplicationMapper.xml

<select id="getStep1Data" resultType="com.zskk.pacsonline.modules.apply.dto.ApplyStep1ResponseDTO">
    SELECT
        e.id,
        e.patient_num AS patientNum,
        e.exam_datetime AS examDatetime,
        e.exam_status AS examStatus,
        e.patient_id AS patientId,
        e.study_id AS studyId,
        e.exam_class AS examClass,
        e.accession_num AS accessionNum,
        e.exam_project AS examProject,
        e.name,
        e.age,
        e.sex,
        e.phone,
        e.birthday,
        e.card_num AS cardNum,
        e.body_part AS bodyPart,
        e.body_part_text AS bodyPartText,
        e.clin_symp AS clinSymp,
        e.clin_diag AS clinDiag,
        r.exam_sub_class AS examSubClass,
        r.device_name AS device,
        r.illness_desc AS illnessDesc,
        r.phys_sign AS physSign,
        r.anamnesis,
        r.family_ill AS familyIll,
        r.application_department AS remoteDepartment,
        r.ext AS remark
    FROM exams e
    LEFT JOIN register r ON r.exam_id = e.id
    WHERE e.id = #{examId}
</select>

3. Service 实现

@Service
@Slf4j
public class ApplyServiceImpl implements ApplyService {

    @Resource
    private RemoteApplicationMapper remoteApplicationMapper;

    @Resource
    private OrgMapper orgMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ApplyStep1ResponseDTO getStep1Data(ApplyStep1RequestVO requestVO) {
        String examId = requestVO.getId();
        String raId = requestVO.getRaId();

        // 情况1:编辑已有申请
        if (StringUtils.isNotBlank(raId)) {
            RemoteApplication application = remoteApplicationMapper.selectById(raId);
            if (application == null) {
                throw new RuntimeException("申请不存在");
            }

            ApplyStep1ResponseDTO dto = new ApplyStep1ResponseDTO();
            BeanUtils.copyProperties(application, dto);
            dto.setApplicationId(raId);
            return dto;
        }

        // 情况2:新建申请
        // 1. 查询检查数据
        ApplyStep1ResponseDTO dto = remoteApplicationMapper.getStep1Data(examId);
        if (dto == null) {
            throw new RuntimeException("检查不存在");
        }

        // 2. 获取当前用户机构信息
        String currentOrgId = getCurrentUserOrgId();
        Org org = orgMapper.selectById(currentOrgId);
        if (org == null) {
            throw new RuntimeException("机构不存在");
        }

        // 3. 创建 remote_application 记录
        RemoteApplication application = new RemoteApplication();
        application.setExamId(examId);
        application.setLocalInstitutionId(currentOrgId);
        application.setLocalInstitutionName(org.getName());
        application.setReqDateTime(new Date());
        application.setName(dto.getName());
        application.setAge(dto.getAge());
        application.setSex(dto.getSex());
        application.setPatientNum(dto.getPatientNum());
        application.setAccessionNum(dto.getAccessionNum());
        application.setReportStatus(1);  // 初始状态
        application.setExamClass(dto.getExamClass());
        application.setPhone(dto.getPhone());
        application.setStudyId(dto.getStudyId());

        // MyBatis-Plus 自动生成 UUID
        remoteApplicationMapper.insert(application);

        // 4. 返回数据中包含 application_id
        dto.setApplicationId(application.getId());

        log.info("创建远程申请成功,examId={}, applicationId={}", examId, application.getId());
        return dto;
    }

    /**
     * 获取当前用户机构ID
     */
    private String getCurrentUserOrgId() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication == null || !(authentication.getPrincipal() instanceof LoginUser)) {
            throw new RuntimeException("未找到登录用户信息");
        }
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        return loginUser.getUser().getCurrentOrg();
    }
}

4. Controller

@RestController
@RequestMapping("/api/apply")
@Tag(name = "远程申请管理", description = "远程诊断申请相关接口")
@Validated
public class ApplyController {

    @Resource
    private ApplyService applyService;

    @Operation(
        summary = "获取申请Step1数据",
        description = "获取远程申请第一步的数据,用于初始化表单。" +
                      "新建申请时只传id,系统会自动创建remote_application记录;" +
                      "编辑申请时传id+raId,返回已有申请数据"
    )
    @ApiResponses(value = {
        @ApiResponse(responseCode = "200", description = "查询成功"),
        @ApiResponse(responseCode = "400", description = "参数错误"),
        @ApiResponse(responseCode = "500", description = "服务器内部错误")
    })
    @SystemLogHandler("获取申请Step1数据|查询")
    @PostMapping("/step1")
    public RestResult<?> getStep1Data(
            @RequestBody @Valid ApplyStep1RequestVO requestVO) {
        try {
            ApplyStep1ResponseDTO dto = applyService.getStep1Data(requestVO);
            return RestResult.ok("查询成功", dto);
        } catch (RuntimeException e) {
            return RestResult.error(e.getMessage());
        } catch (Exception e) {
            return RestResult.error("查询失败:" + e.getMessage());
        }
    }
}

注意事项

  1. 自动创建申请: 新建时会自动插入 remote_application 记录,状态为1
  2. 联表查询: exams 和 register 是LEFT JOIN,register可能为空
  3. UUID生成: application_id 使用 UUID,确保唯一性
  4. 机构信息: 从token获取当前用户的机构ID和名称
  5. 事务控制: Service层方法需要 @Transactional 注解
  6. 字段映射: 注意SQL字段别名与DTO字段名的对应(驼峰命名)