注意事项

1. 请求方式: HTTP + JSON + POST / GET

2. 字段名均为小写

3. 统一使用UTF-8编码

4. 春雨测试服务地址 https://test.chunyutianxia.com/,正式服务地址 https://www.chunyuyisheng.com/

5. 第三方用户可以使用管理后台进行自测,该管理账户请联系相关春雨人员开通

6. 春雨所有环境的机房出口IP,如需增加到白名单,请使用:

  • 所有云主机默认外网(natgw): 123.59.151.95
  • 物理机外网(代理vpn), 123.59.151.126
  • 云主机重要线上业务,117.50.107.208/28,即117.50.107.208-117.50.107.223共16个ip地址

7. 合作方需按照跟春雨商定的合作方式来开发,不能接入未合作的问诊方式

8. 关于医生总结:API渠道图文问题关闭后,医生仍可以发送总结消息(以医生文字回复形式)来通知患者(关闭后患者不可复),如存在医生在问题关闭后发总结情况,请确保可以接收到春雨推送信息并及时通知患者;

9. 相关服务开通权限后方可使用

签名校验机制

双方进行通信时,使用加密密钥(partner_key)进行安全校验, 签名密钥值用sign标识,需严格保密。

签名生成规则按使用场景分为两类:

  • 非回调类型:使用key、atime、user_id生成sign值
  • 回调接口类型:使用key、atime、problem_id生成sign值(电话类服务使用service_id替换掉problem_id)

非回调类型生成方式示例如下:

# PYTHON 版本 生成方法
                import hashlib
                # 合作方 partner_key,注意不是 partner
                partner_key = 'XKBP1Oqut0r2LiGV'
                # UNIX TIMESTAMP 最小单位为秒
                atime = '1467098815'
                # 第三方用户唯一标识,可以为字母与数字组合的字符串
                user_id = 'A800130'
                # 获得签名: md5的32位结果取中间16位
                sign = hashlib.md5(partner_key + atime + user_id).hexdigest()[8:-8]
                # 输出sign:5afda19c5d65a7a7
                print sign
                
// PHP 版本 生成方法
                // 合作方 partner_key, 注意不是 partner
                $partner_key = "XKBP1Oqut0r2LiGV";
                // UNIX TIMESTAMP 最小单位为秒
                $atime = "1467098815";
                // 第三方用户唯一标识,可以为字母与数字组合的字符串。
                $user_id = "A800130";
                // 生成签名 5afda19c5d65a7a7
                $sign = substr(md5($partner_key.$atime.$user_id), 8, 16);
                // 输出sign:5afda19c5d65a7a7
                echo "sign: ".$sign;
                
// JAVA 版本 生成方法
                import java.security.MessageDigest;
                import java.security.NoSuchAlgorithmException;
                import org.apache.commons.codec.binary.Hex;

                public class SignTest {

                    // 计算 Sign
                    private static String getSign(String partner_key, String atime, String user_id)
                                throws NoSuchAlgorithmException{
                        String info = partner_key + atime + user_id;
                        MessageDigest md5 = MessageDigest.getInstance("MD5");
                        byte[] srcBytes = info.getBytes();
                        md5.update(srcBytes);
                        byte[] resultBytes = md5.digest();
                        String resultString = new String(new Hex().encode(resultBytes));
                        return resultString.substring(8, 24);
                    }

                    public static void main( final String[] args ){
                        try {
                            // 合作方 partner_key, 注意不是 partner
                            String partner_key = "XKBP1Oqut0r2LiGV";
                            // UNIX TIMESTAMP 最小单位为秒
                            String atime = "1467098815";
                            // 第三方用户唯一标识,可以为字母与数字组合的字符串。
                            String user_id = "A800130";
                            // 计算sign结果为: 5afda19c5d65a7a7
                            String sign = SignTest.getSign(partner_key, atime, user_id);
                            System.out.println(sign);
                        }
                        catch (Exception ex)
                        {
                            ex.printStackTrace();
                        }

                    }
                }
                
// C# 版本 生成方法
                using System;
                using System.Security.Cryptography;
                using System.Text;

                public class Test
                {
                    public static void Main()
                    {
                        // C#版本生成方法
                        string partner_key = "XKBP1Oqut0r2LiGV";
                        // UNIX TIMESTAMP 最小单位为秒
                        string atime = "1467098815";
                        // 第三方用户唯一标识,可以为字母与数字组合的字符串
                        string user_id = "A800130";
                        // 获得签名: md5的32位结果取中间16位
                        MD5 md5 = new MD5CryptoServiceProvider();
                        byte[] output = md5.ComputeHash(Encoding.UTF8.GetBytes(partner_key+atime+user_id));
                        string sign = BitConverter.ToString(output).Replace("-","").Substring(8, 16).ToLower();
                        // 输出sign:5afda19c5d65a7a7
                        Console.WriteLine(sign);
                    }
                }
                

提问数据结构

JSONArray集合,格式: [contentItem 1, contentItem 2,..., contentItem n]

contentItem含义:

  • 文字内容 {'type': 'text', 'text': '这是一段文本形式的内容'}
  • 图片内容 {'type': 'image', 'file': '这是图片形式的内容,这里是图片的 url'}
  • 音频内容 {'type': 'audio', 'file': '这是语音形式的内容,这里是音频文件的 url'}
  • 视频内容 {'type': 'video', 'file': '这是视频形式的内容,这里是视频文件的 url','duration':10, 'partner_cover_image':'视频封面图url'}
  • 病人资料 {'type': 'patient_meta', 'age': '15 岁', 'sex': '男', 'name': '张三'}

生成content代码示例如下:

说明:

  • 众包服务为确保分诊准确性,患者首问字数请限制在10-500字;且务必传给春雨提问患者姓别、年龄字段;
  • 创建问题时首问请务必提问医疗问题,非医疗问题会被举报;
  • 图片文件要求为JPG或PNG,单张最大不超过5M,每次最多可同时上传9张,多张图片时使用多个contentItem;
  • 音频文件格式要求是MP3,最长两分钟;
  • 视频文件要求为.mp4格式,时长不超过1分钟。duration单位为秒;
  • 提交的文件URL,春雨目前不会下载保存,请确保提交的文件URL可以长期稳定访问
  • 使用JSON POST时,请注意content类型为string而不是嵌套的json类型
# PYTHON 版本 生成方法
import json
# 创建对话内容
content = [
    {"type": "text","text": "这是一段文本形式的内容"},
    {"type": "image","file": "这是图片形式的内容,这里是图片的 url'"},
    {"type": "audio","file": "这是语音形式的内容,这里是音频文件的 url"},
    {"type": "video","file": "这是视频形式的内容,这里是视频文件的 url","duration": 10,"partner_cover_image": "视频封面图url"},
    {"type": "patient_meta","age": "15岁","sex": "男","name": "张三"}
]
# 获得json string 格式结果,接口中content字段使用该结果
content = json.dumps(content)
// 创建对话内容
import json
// 创建对话内容
$content_list = array(
    array ('type'=>'text','text'=>'这是一段文本形式的内容'),
    array ('type'=>'image','file'=>'这是图片形式的内容,这里是图片的 url'),
    array ('type'=>'audio','file'=>'这是语音形式的内容,这里是音频文件的 url'),
    array ('type'=>'video','file'=>'这是视频形式的内容,这里是视频文件的 url','duration':10'),
    array ('type'=>'patient_meta','age'=>'15 岁', 'sex'=>'男'),
);
// 获得json string 格式结果,接口中content字段使用该结果
echo json_encode($content_list);
// JAVA 版本 生成方法
import java.text.ParseException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import net.sf.json.JSONArray;
import net.sf.json.JSONException;

public class JsonTest {

    public static void main(String[] args) throws JSONException, ParseException {
        ArrayList content_list = new ArrayList();
        // 添加文本内容
        Map c_text = new HashMap();
        c_text.put("type", "text");
        c_text.put("text", "这是一段文本形式的内容");
        content_list.add(c_text);
        // 添加图片内容
        Map c_image = new HashMap();
        c_image.put("type", "image");
        c_image.put("file", "这是图片形式的内容,这里是图片的 url");
        content_list.add(c_image);
        // 添加音频内容
        Map c_audio = new HashMap();
        c_audio.put("type", "audio");
        c_audio.put("file", "这是一段文本形式的内容,这里是音频文件的 url");
        content_list.add(c_audio);
        // 添加视频内容
        Map c_video = new HashMap();
        c_video.put("type", "video");
        c_video.put("file", "这是视频形式的内容,这里是视频文件的 url','duration':10'");
        content_list.add(c_video);
        // 添加患者信息
        Map c_patient_meta = new HashMap();
        c_patient_meta.put("type", "patient_meta");
        c_patient_meta.put("age", "15 岁");
        c_patient_meta.put("sex", "男");
        content_list.add(c_patient_meta);

        // 输出结果
        JSONArray content_json = JSONArray.fromObject(content_list);
        String content = content_json.toString();
        System.out.println(content);
    }
}

科室划分

一级科室编码 具体科室 二级科室编码 具体科室
1 妇科 妇科
2 儿科 fb 新生儿科
fa 小儿科
3 内科 ai 感染科
ah 血液病科
ag 风湿免疫科
af 内分泌与代谢科
ae 肾内科
ad 消化内科
ac 神经内科
ab 心血管内科
aa 呼吸内科
4 皮肤性病科 hb 性病科
ha 皮肤科
6 营养科 营养科
7 骨伤科 cc 创伤科
cb 关节科
ca 脊柱科
8 男科 男科
9 外科 bj 甲状腺乳腺外科
bi 普外科
bh 肛肠科
bg 泌尿外科
bf 康复科
be 烧伤科
bd 肝胆外科
bc 神经外科
bb 心脏与血管外科
ba 胸外科
11 肿瘤及防治科 md 肿瘤中医科
mc 介入与放疗中心
mb 肿瘤外科
ma 肿瘤内科
12 中医科 oe 中医儿科
od 中医男科
oc 中医妇科
ob 中医外科
oa 中医内科
13 口腔颌面科 口腔颌面科
14 耳鼻咽喉科 jc 咽喉科
jb 鼻科
ja 耳科
15 眼科
16 整型美容科
17 精神心理科 nb 心理科
na 精神科
21 产科
22 报告解读科 qi 预防保健科
qh 体检中心
qg 麻醉科
qf 超声科
qe 心电图科
qd 病理科
qc 内镜科
qb 放射科
qa 检验科

Postman调试工具

Postman接口测试套件使用说明

使用此工具,可以在开发未完成状态下,验证接口服务状态和业务的接入流程,同时在开发过程中,如果遇到问题,报错等,可以快速帮助定位问题。只需要考虑业务参数,而不需要关注签名,时间戳等。

注意:虽然可配置线上的请求,但请慎重使用,如随意测试线上接口可能会导致用户id被拉黑,多扣费等情况。

使用和配置

1.下载Postman > 7.0版本

2.下载Postman_config.zip并解压

3.Import->Folder 选择文件夹导入

在环境变量配置(1)->全局配置(2)中修改partner

在环境变量配置中的test(3)、online(4)中配置测试环境、线上环境的partner_key和当前环境的测试用户。

注意:线上测试必须添加user_id至白名单

在选择好测试环境后,选择需要测试的接口进行请求即可,测试过程中只需要修改业务参数如partner_order_id,problem_id等。不需要再关注签名、时间戳等验证字段

业务问题

1、问题交互次数定义

答:患者连续追问直至医生回答定义为一次交互。示意图(以下三种情况均定义为一次交互):

问题交互次数定义

2、服务自动关闭逻辑?

图文问题医生首次答复后会重新计算时间,后续答复则不再重新计算。

问题类型 已回复自动关闭逻辑 未回复自动关闭逻辑 测试环境自动关闭时间 关闭后的问题状态
众包服务 20次交互或医生首次回复24h后问题关闭 24h自动关闭 已回复/未回复均为1h 关闭状态
众包升级 20次交互或医生首次回复24h后问题关闭 1h自动关闭 已回复/未回复均为1h 关闭状态
定向图文 30次交互或医生首次回复48h后问题关闭 24h自动关闭 已回复/未回复均为1h 关闭状态
名医咨询 10次交互或医生首次回复48h后问题关闭 24h自动关闭 已回复/未回复均为1h 关闭状态
图文急诊 20次交互或医生首次回复30min后问题关闭 30min自动关闭 已回复/未回复均为30min 关闭状态

3、健康档案需要提供哪些字段信息?

答:API对接方式下,健康档案包括:性别、年龄。具体可参考 开发前必读->提问数据结构

4、春雨平台侧能否提供科室信息?

答:目前春雨平台侧可以提供一级/二级科室信息,科室列表信息请参见:API接口->定向图文服务->找医生接口。

5、H5和API的对接方式下,收款流程都是什么样的?

答:如以H5形式对接,春雨直接收费。如以API形式对接,用户直接支付给合作方,合作方再和春雨平台定期结算。

6、API接入众包服务:用户提交问题后,用户对系统分配到的科室可以修改吗?

答:不可以修改。

7、允许用户进行评价的触发条件是什么?

答:医患交互后即可评价医生。

8、针对医生评价功能,不满意的原因是必填项么?

答:必填。

9、针对举报功能:被医生或系统举报的功能,有专门的接口么?用户被举报后,如何通知到合作方?

答:被医生或系统举报,无专门的接口通知。会通过【通用接口】的【问题关闭通知】发close或refund通知。春雨会返回对应的被举报文案,请参见【退款及举报逻辑】部分。同时被举报后会以特殊的问题状态进行标识,参见【问题详情接口】。

10、针对黑名单功能:当用户被列入黑名单时,用户再提问时,会如何提示?

答:用户被加入黑名单后,当该用户提问时,春雨平台会返回错误码12003来进行标识。具体请参见【黑名单逻辑】和【错误码】中的对应内容。

技术问题

1、为何在安卓客户端的webview中点击上传图片控件没反应?

答: 由于安全因素android webview屏蔽了文件上传控件,具体参考:

https://www.oschina.net/question/23880_50205
安卓5.0及以上版本中,有修改,具体参考:
http://blog.csdn.net/u012912435/article/details/51484211
在在Android5.0及以上版本中使用WebView加载https资源文件时,
如果认证证书不被Android认可,那么会出现无法成功加载对应资源问题。
具体解决参考:
https://www.cnblogs.com/zhengshiqiang47/p/6220295.html

2、为何在H5页面点击创建问题无效?

答: H5页面不能在PC浏览器环境下直接使用,需要在手机或手机模拟器环境下使用。

3、为何在加载H5页面时候样式丢失?

答: H5页面所有资源URL为相对URL,请确认当前加载环境是否支持获取该资源。

4、什么是atime?

答: atime 为时间戳,是指格林威治时间1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至现在的总秒数;atime需实时获取,前后15min有效。

5、为何在回调通知里面查不到相关参数?

答: 春雨发送第三方回调通知时采用POST方式,data为json格式,请确保接口符合要求。如果仍然未获取到相关参数,建议增加request请求内容的完整打印消息来排查原因。

6、每次API接口 创建/追问 问题前都需要先请求账号同步接口吗?

答: 不需要,新用户只要同步过一次即可。

7、H5不能直接返回到上级页面吗?

答: 由于H5是嵌入第三方APP的,所以返回栏需要由第三方来实现。

8、API付费款项是怎样处理的?

答: 第三方需要先在春雨服务器创建付费问诊记录,然后引导用户付费至第三方平台,成功后通知春雨服务器更新订单,后续问诊交互同API免费问诊一致。春雨公司定期与第三方公司结算相关款项。

9、接口返回"invalid_user"是为什么?

答: 首先 user_id 只可以包含大小写字母与数字、下划线的组合,其次在确认 user_id 符合格式时请检查是否是由于构建的数据类型(content或者assess_info)格式错误,导致参数乱码(可通过抓包的方式确认发出的数据是否正确)。

10、H5接入方式下,针对Android系统,填写病情描述后不能点击下一步的解决办法?

答: 参考如下代码进行解决
webSettings.setJavaScriptEnabled(true);
webSettings.setDomStorageEnabled(true);
// 前面俩个必须同时在
webView.setWebChromeClient(new MyWebChromeClient());
webSettings.setAllowFileAccess(true)

11、API对接众包升级服务:可以使用哪些接口?。

答:请查看【开发文档】中,【众包升级服务】目录下的所有接口,【通用接口】下的所有接口,和【第三方回调管理】的所有接口。