关于京东溯源数据上传的心得
京东溯源数据上传,也是碰到好多问题,现在整理下,给后面的人一个思路
开发语言:java
开发工具:sts
相关技术:StringBootMvc、myBatis、shiro、thymeleaf等
一、注册宙斯系统并申请应用
宙斯地址:https://jos.jd.com
创建应用:https://jos.jd.com/commondoc?listId=160
二、下载SDK并导入本地库
我用的是maven,所以需要把下载的java 京东SDKjar包导入到本地maven库中
mvn install:install-file -Dfile=open-api-sdk-2.0-2022-03-23.jar -DgroupId=jd.open.api -DartifactId=jdsdk2 -Dversion=2.0.0 -Dpackaging=jar
-Dfile:jar包所在本地的具体路径
-DgroupId:项目组织唯一的标识符,实际对应JAVA的包的结构
-DartifactId:项目的唯一的标识符,实际对应项目的名称,就是项目根目录的名称
-Dversion:版本号
-Dpackaging:打包的类型
运行后出现BUILD SUCCESS就表示导入成功了,在pom.xml里引用即可。
三、获取code
在创建好的应用审核通过,点击【管理】获取到改应用的appKey和appSecret,可用配置到数据库中。
在应用设置里,配置回调URL,如果没有回调URL可用设置成功http://www.baidu.com
拼接连接获取code:
拼接的连接:https://open-oauth.jd.com/oauth2/to_login?app_key=appKey&response_type=code&redirect_uri=回调URL&state=20220331&scope=snsapi_base
app_key:对应应用的appKey
redirect_uri: 回调URL地址
state:当日时间 20220331
1、通过回调URL获取code
此方式需要自己在写一个回调方法,确保地址能访问通,回调方法中获取code后直接在此获取accessToken;
回调方法代码:
1 @GetMapping("/jdSyReceipt") 2 public void jdSyReceipt(HttpServletRequest request, HttpServletResponse response) 3 { 4 String appKey = "appkey"; // 应用key 5 String appSecret = "appSecret"; // 应用秘钥 6 // 获取地址栏里的code 7 String code = request.getParameter("code"); 8 System.out.println(code); 9 10 // 根据类型获取应用访问令牌 11 OutsideAccessToken accessToken = outsideAccessTokenService.selectOutsideAccessTokenByType("jd_suyuan"); 12 13 String to_token_url = "https://open-oauth.jd.com/oauth2/access_token"; 14 String token_param = "app_key="+appKey+"&app_secret="+appSecret+"&grant_type=authorization_code&code="+code; 15 16 // 通过url和参数,通过httpget方式请求获取accessToken 17 String jsonToken = HttpUtils.sendGet(to_token_url, token_param); 18 // 把JSON字符串转成成对象 import com.alibaba.fastjson.JSON; 19 JdAccessToken jdAccessToken = (JdAccessToken) JsonUtil.parseObject(jsonToken, JdAccessToken.class); 20 if(jdAccessToken.getCode().equals("0")) { 21 // 判断京东令牌是否为空 22 if(StringUtils.isNotNull(accessToken)) { // 不为空 23 accessToken.setAccessToken(jdAccessToken.getAccess_token()); 24 accessToken.setExpiresIn(jdAccessToken.getExpires_in()); 25 accessToken.setRefreshToken(jdAccessToken.getRefresh_token()); 26 accessToken.setScope(jdAccessToken.getScope()); 27 accessToken.setOpenId(jdAccessToken.getOpen_id()); 28 accessToken.setUid(jdAccessToken.getUid()); 29 if(null != jdAccessToken.getTime()) { 30 Date date = new Date(jdAccessToken.getTime()); 31 accessToken.setTime(date); 32 } 33 accessToken.setTokenType(jdAccessToken.getToken_type()); 34 accessToken.setCode(jdAccessToken.getCode()); 35 accessToken.setXid(jdAccessToken.getXid()); 36 outsideAccessTokenService.updateOutsideAccessToken(accessToken); 37 log.info("jdSyReceipt:结果》》》》更新成功"); 38 }else { // 为空 39 accessToken.setAccessToken(jdAccessToken.getAccess_token()); 40 accessToken.setExpiresIn(jdAccessToken.getExpires_in()); 41 accessToken.setRefreshToken(jdAccessToken.getRefresh_token()); 42 accessToken.setScope(jdAccessToken.getScope()); 43 accessToken.setOpenId(jdAccessToken.getOpen_id()); 44 accessToken.setUid(jdAccessToken.getUid()); 45 if(null != jdAccessToken.getTime()) { 46 Date date = new Date(jdAccessToken.getTime()); 47 accessToken.setTime(date); 48 } 49 accessToken.setTokenType(jdAccessToken.getToken_type()); 50 accessToken.setCode(jdAccessToken.getCode()); 51 accessToken.setXid(jdAccessToken.getXid()); 52 accessToken.setTypeName("jd_suyuan"); 53 outsideAccessTokenService.insertOutsideAccessToken(accessToken); 54 log.info("jdSyReceipt:结果》》》》添加成功"); 55 } 56 }else { 57 //return AjaxResult.error(jdAccessToken.getMsg()); 58 log.info("jdSyReceipt:结果》》》》"+jdAccessToken.getMsg()); 59 } 60 }
2、浏览器地址栏获取code
此方式需要在浏览器地址栏获取到code,获取code后在拼接获取accessToken的连接,成功后获取JSON字符串。
四、获取accessToken
1、通过回调URL获取,第三获取code时有代码
2、自己拼接连接:https://open-oauth.jd.com/oauth2/access_token?app_key=appKey&app_secret=appSecret&grant_type=authorization_code&code=code
访问后获取JSON字符串,不建议此方式。
五、使用SDK上传溯源数据
先说下我这边的逻辑:先通过订单查询改单据的罐数,因为京东SDK上传数据量要求1500条,我们组合的list时一罐产品对应4条(流通信息、生产信息、商品信息、检测信息),一次上传要限定下罐数,我这边设置的是360罐
看下列代码:
以下代码只供参考,获取accessToken这个要根据自己实际情况来。TraceDataItem 这个包含四个字段:key、val、fileName、fileType,可以根据自己公司的实际情况来命名和上传。
public AjaxResult syncJdSuyuan() { try { String traceaccount = "自己的溯源账号"; // 溯源账号 String industryCode = "jdbiz"; //行业编码 // 获取第4部时的accessToken信息 OutsideAccessToken accessToken = outsideAccessTokenService.selectOutsideAccessTokenByType("jd_suyuan"); String appKey = "应用的appkey"; String appSecret = "应用的秘钥"; if(StringUtils.isNotNull(accessToken)) { /// 我这是根据订单查询发往京东的罐数 List<BillDistDetail> ListBillDetails = billDistDetailService.selectBillDistDetailListByInBocde("订单号"); // 增加线程锁,避免循环提交是错乱 synchronized (obj) { /*** 批量处理 list */ int pointsDataLimit = 360;// 限制条数 int part = ListBillDetails.size() / pointsDataLimit;// 分批数 // 分批处理结果为OK的list条码对象 if(ListBillDetails.size() > pointsDataLimit) { for (int i = 0; i < part; i++) { List<TraceData> record = new ArrayList<TraceData>(); // 获取分批后的条码list List<BillDistDetail> listPageCodes = ListBillDetails.subList(0, pointsDataLimit); // 循环遍历分割后list的产品信息 for (BillDistDetail billDetail : listPageCodes) { String uniquecode = "自己的唯一产品码"; //流通信息:17137 生产信息:17138 商品信息:17139 检测信息:17140 /** 流通信息17137(原产地、品牌出库仓库、收货仓库) Start */ TraceData ltData= new TraceData(); ltData.setUniquecode(uniquecode); // 追溯编码 ltData.setTraceaccount(traceaccount); // 追溯账号 List<TraceDataItem> ltDataList = new ArrayList<TraceDataItem>(); TraceDataItem ycd = new TraceDataItem(); ycd.setKey("原产地"); ycd.setFiledName("countryOrigin"); ycd.setFiledType("text"); ycd.setVal(""); // 溯源信息原产地 ltDataList.add(ycd); TraceDataItem ppckck = new TraceDataItem(); ppckck.setKey("品牌出库仓库"); ppckck.setFiledName("exWarehouse"); ppckck.setFiledType("text"); ppckck.setVal("成品库"); ltDataList.add(ppckck); TraceDataItem shck = new TraceDataItem(); shck.setKey("收货仓库"); shck.setFiledName("receivingWarehouse"); shck.setFiledType("text"); shck.setVal("北京京东世纪信息技术有限公司"); ltDataList.add(shck); ltData.setDataList(ltDataList); //追溯流通信息详情 ltData.setDataType(17137); //追溯流通信息环节 ltData.setSkuid(1000L); //京东商品ID ltData.setIndustryCode(industryCode); //行业编码 record.add(ltData); /** 流通信息17137 End */ /** 生产信息17138(生产批号、生产日期、保质期、有效日期) Start */ TraceData scData= new TraceData(); scData.setUniquecode(uniquecode); // 追溯编码 scData.setTraceaccount(traceaccount); // 追溯账号 List<TraceDataItem> scdataList = new ArrayList<TraceDataItem>(); TraceDataItem scpcItem = new TraceDataItem(); scpcItem.setKey("生产批号"); scpcItem.setFiledName("BatchNumber"); scpcItem.setFiledType("text"); scpcItem.setVal("生产批次"); scdataList.add(scpcItem); TraceDataItem scrqItem = new TraceDataItem(); scrqItem.setKey("生产日期"); scrqItem.setFiledName("produceDate"); scrqItem.setFiledType("text"); scrqItem.setVal("生产日期"); scdataList.add(scrqItem); TraceDataItem bzqItem = new TraceDataItem(); bzqItem.setKey("保质期"); bzqItem.setFiledName("shelfLife"); bzqItem.setFiledType("text"); bzqItem.setVal("730"); scdataList.add(bzqItem); TraceDataItem yxrqItem = new TraceDataItem(); yxrqItem.setKey("有效日期"); yxrqItem.setFiledName("effectiveDate"); yxrqItem.setFiledType("text"); yxrqItem.setVal("有效日期"); scdataList.add(yxrqItem); scData.setDataList(scdataList); //追溯生产信息详情 scData.setDataType(17138); //追溯生产信息环节 scData.setSkuid(1000L); //京东商品ID scData.setIndustryCode(industryCode); //行业编码 record.add(scData); /** 生产信息17138(生产批号、生产日期、保质期、有效日期) End */ /** 商品信息17139(产品检验报告)Start */ TraceData spData= new TraceData(); spData.setUniquecode(uniquecode); // 追溯编码 spData.setTraceaccount(traceaccount); // 追溯账号 spData.setDataType(17139); //追溯商品信息环节 spData.setSkuid(1000L); //京东商品ID spData.setIndustryCode(industryCode); //行业编码 List<TraceDataItem> spDataList = new ArrayList<TraceDataItem>(); // TraceDataItem ppmcItem = new TraceDataItem(); // ppmcItem.setKey("品牌名称"); // ppmcItem.setFiledName("brandName"); // ppmcItem.setFiledType("text"); // ppmcItem.setVal(jdSuyuanGoods.getBrandName()); // spDataList.add(ppmcItem); TraceDataItem xlItem = new TraceDataItem(); xlItem.setKey("系列"); xlItem.setFiledName("series"); xlItem.setFiledType("text"); xlItem.setVal("产品系列"); spDataList.add(xlItem); TraceDataItem dwItem = new TraceDataItem(); dwItem.setKey("段位"); dwItem.setFiledName("goodsLevel"); dwItem.setFiledType("text"); dwItem.setVal("产品段位"); spDataList.add(dwItem); spData.setDataList(spDataList); //追溯商品信息详情 record.add(spData); /** 商品信息17139(产品检验报告)End */ /** 检测信息17140(产品检验报告)Start */ TraceData jyData= new TraceData(); jyData.setUniquecode(uniquecode); // 追溯编码 jyData.setTraceaccount("haiwangshipin"); // 追溯账号 List<TraceDataItem> jyDataList = new ArrayList<TraceDataItem>(); TraceDataItem zjbgItem = new TraceDataItem(); zjbgItem.setKey("产品质检报告"); zjbgItem.setFiledName("inspectionReportImg"); zjbgItem.setFiledType("img"); zjbgItem.setVal("检测报告httpsURL地址");// 产品质检报告 jyDataList.add(zjbgItem); jyData.setDataList(jyDataList); //追溯检测信息详情 jyData.setDataType(17140); //追溯检测信息环节 jyData.setSkuid(1000L); //京东商品ID jyData.setIndustryCode(industryCode); //行业编码 record.add(jyData); /** 检测信息17140(产品检验报告)End */ }// 请求京东 JdClient client = new DefaultJdClient("https://api.jd.com/routerjson", accessToken.getAccessToken(), appKey, appSecret); BtBlockchainMiddleTraceDataVendorServiceGateWayInsertBatchRequest request = new BtBlockchainMiddleTraceDataVendorServiceGateWayInsertBatchRequest(); request.setRecord(record); BtBlockchainMiddleTraceDataVendorServiceGateWayInsertBatchResponse response = client.execute(request); // 打印上传数据结果,依此判断是否提交成功 System.out.println(response.getMsg()); // 清除分批处理的数据 ListBillDetails.subList(0, pointsDataLimit).clear(); } } // 剩余部分 if(!ListBillDetails.isEmpty()) { List<TraceData> record = new ArrayList<TraceData>(); // 为避免accessToken在循环中失效,再次获取 OutsideAccessToken accessToken2 = outsideAccessTokenService.selectOutsideAccessTokenByType("jd_suyuan"); for (BillDistributorDetail billDetail : ListBillDetails) { String uniquecode = "自己的唯一产品码"; //流通信息:17137 生产信息:17138 商品信息:17139 检测信息:17140 /** 流通信息17137(原产地、品牌出库仓库、收货仓库) Start */ TraceData ltData= new TraceData(); ltData.setUniquecode(uniquecode); // 追溯编码 ltData.setTraceaccount(traceaccount); // 追溯账号 List<TraceDataItem> ltDataList = new ArrayList<TraceDataItem>(); TraceDataItem ycd = new TraceDataItem(); ycd.setKey("原产地"); ycd.setFiledName("countryOrigin"); ycd.setFiledType("text"); ycd.setVal(""); // 溯源信息原产地 ltDataList.add(ycd); TraceDataItem ppckck = new TraceDataItem(); ppckck.setKey("品牌出库仓库"); ppckck.setFiledName("exWarehouse"); ppckck.setFiledType("text"); ppckck.setVal("成品库"); ltDataList.add(ppckck); TraceDataItem shck = new TraceDataItem(); shck.setKey("收货仓库"); shck.setFiledName("receivingWarehouse"); shck.setFiledType("text"); shck.setVal("北京京东世纪信息技术有限公司"); ltDataList.add(shck); ltData.setDataList(ltDataList); //追溯流通信息详情 ltData.setDataType(17137); //追溯流通信息环节 ltData.setSkuid(1000L); //京东商品ID ltData.setIndustryCode(industryCode); //行业编码 record.add(ltData); /** 流通信息17137 End */ /** 生产信息17138(生产批号、生产日期、保质期、有效日期) Start */ TraceData scData= new TraceData(); scData.setUniquecode(uniquecode); // 追溯编码 scData.setTraceaccount(traceaccount); // 追溯账号 List<TraceDataItem> scdataList = new ArrayList<TraceDataItem>(); TraceDataItem scpcItem = new TraceDataItem(); scpcItem.setKey("生产批号"); scpcItem.setFiledName("BatchNumber"); scpcItem.setFiledType("text"); scpcItem.setVal("生产批次"); scdataList.add(scpcItem); TraceDataItem scrqItem = new TraceDataItem(); scrqItem.setKey("生产日期"); scrqItem.setFiledName("produceDate"); scrqItem.setFiledType("text"); scrqItem.setVal("生产日期"); scdataList.add(scrqItem); TraceDataItem bzqItem = new TraceDataItem(); bzqItem.setKey("保质期"); bzqItem.setFiledName("shelfLife"); bzqItem.setFiledType("text"); bzqItem.setVal("730"); scdataList.add(bzqItem); TraceDataItem yxrqItem = new TraceDataItem(); yxrqItem.setKey("有效日期"); yxrqItem.setFiledName("effectiveDate"); yxrqItem.setFiledType("text"); yxrqItem.setVal("有效日期"); scdataList.add(yxrqItem); scData.setDataList(scdataList); //追溯生产信息详情 scData.setDataType(17138); //追溯生产信息环节 scData.setSkuid(1000L); //京东商品ID scData.setIndustryCode(industryCode); //行业编码 record.add(scData); /** 生产信息17138(生产批号、生产日期、保质期、有效日期) End */ /** 商品信息17139(产品检验报告)Start */ TraceData spData= new TraceData(); spData.setUniquecode(uniquecode); // 追溯编码 spData.setTraceaccount(traceaccount); // 追溯账号 spData.setDataType(17139); //追溯商品信息环节 spData.setSkuid(1000L); //京东商品ID spData.setIndustryCode(industryCode); //行业编码 List<TraceDataItem> spDataList = new ArrayList<TraceDataItem>(); // TraceDataItem ppmcItem = new TraceDataItem(); // ppmcItem.setKey("品牌名称"); // ppmcItem.setFiledName("brandName"); // ppmcItem.setFiledType("text"); // ppmcItem.setVal(jdSuyuanGoods.getBrandName()); // spDataList.add(ppmcItem); TraceDataItem xlItem = new TraceDataItem(); xlItem.setKey("系列"); xlItem.setFiledName("series"); xlItem.setFiledType("text"); xlItem.setVal("产品系列"); spDataList.add(xlItem); TraceDataItem dwItem = new TraceDataItem(); dwItem.setKey("段位"); dwItem.setFiledName("goodsLevel"); dwItem.setFiledType("text"); dwItem.setVal("产品段位"); spDataList.add(dwItem); spData.setDataList(spDataList); //追溯商品信息详情 record.add(spData); /** 商品信息17139(产品检验报告)End */ /** 检测信息17140(产品检验报告)Start */ TraceData jyData= new TraceData(); jyData.setUniquecode(uniquecode); // 追溯编码 jyData.setTraceaccount("haiwangshipin"); // 追溯账号 List<TraceDataItem> jyDataList = new ArrayList<TraceDataItem>(); TraceDataItem zjbgItem = new TraceDataItem(); zjbgItem.setKey("产品质检报告"); zjbgItem.setFiledName("inspectionReportImg"); zjbgItem.setFiledType("img"); zjbgItem.setVal("检测报告httpsURL地址");// 产品质检报告 jyDataList.add(zjbgItem); jyData.setDataList(jyDataList); //追溯检测信息详情 jyData.setDataType(17140); //追溯检测信息环节 jyData.setSkuid(1000L); //京东商品ID jyData.setIndustryCode(industryCode); //行业编码 record.add(jyData); /** 检测信息17140(产品检验报告)End */ } JdClient client = new DefaultJdClient("https://api.jd.com/routerjson", accessToken.getAccessToken(), appKey, appSecret); BtBlockchainMiddleTraceDataVendorServiceGateWayInsertBatchRequest request = new BtBlockchainMiddleTraceDataVendorServiceGateWayInsertBatchRequest(); request.setRecord(record); BtBlockchainMiddleTraceDataVendorServiceGateWayInsertBatchResponse response = client.execute(request); // // 打印上传数据结果,依此判断是否提交成功 System.out.println(response.getMsg()); } } }else { return AjaxResult.error("访问令牌不存在"); } return AjaxResult.success("执行成功"); } catch (Exception e) { e.printStackTrace(); return AjaxResult.error("运行出现异常:",e.getMessage()); } }
六、上传数据是遇到的一些坑
问题1:应用未发布之前,SDK方式只能提交一次,不能循环提交多次,如果批量上传,需要发布应用。
问题2:如果record的list超过限制也会报错,请合理分割list进行上传。
问题3:如果报时间差异比较大,看下自己服务器和京东服务器时间是否存在误差。误差时间不能超过10分钟。
京东服务器时间查看:https://api.m.jd.com/client.action?functionId=queryMaterialProducts&client=wh5
问题4:检验报告的URL,一定要外网测试是否能装车访问,要用https。
错误详解:https://jos.jd.com/faqdetail?listId=447&itemId=1805
还有千万千万不要用API HTTP方式上传,会遇见更多的问题,我没搞通。
友情提示:最好把应用信息、请求信息、访问令牌信息,都存到数据库里,直接调用获取即可。
【来一场说走就走的旅行,不如学一种说学就学的语言】
作者:小温
出处:https://www.cnblogs.com/wencg/
版权:本文采用「CC BY 4.0」知识共享许可协议进行许可。