前言:其实唯品会的接口使用还是非常简单,开发者可能有疑问还是他们的接口流程问题和如何与自家的ERP对接起来。这篇文章是在记录自己对接唯品会的过程的理解,也因为是第一次接触唯品会,刚开始疑问还挺多的,网上也没啥这类问题的解答,都得自己去研究实验,有需要的可参考参考。

PS:唯品会的开放平台文档N久没更新,有些地方说的已经不存在或者已经变更为其他方式,总之就是看文档会让你产生很多误区,但是你也没地方有资料看了。另外他们的沙箱,提供的数据是全是死的不变的,而且跟实际数据会差很远,我是按自己的理解纯码代码上正式测的。(今年更新了,我这是去年对接的,今年SDK为应对新型肺炎疫情 新增了一些字段,没更新的同志们要注意了)

一、准备工作-秘钥的获取

1.创建应用:这很简单注册成为开发者,供应商认证成功过后就可以创建应用,我创建的是 自研应用 。这里要说明一下,创建应用时填写的选项也是有学问的:

合作类型 -- JIT和直发:

JIT模式:我是服装公司(唯品会称呼我为供应商),客户下单了,由唯品会下指要供应商发货,发到唯品会他们自己的指另的仓库,然后由唯品会的人发货给客户,所以快递费要我们自己出。

直发:客户下单了,由我(供应商)直接发货给客户。PS:这块的接口他们已经弃用了,但是开发者平台没更新。这块的业务已经改为JITX业务了,接口也是另一套。这里也要说明一下:即使是我们自己发货,但你也不要以为你就拥有了自己的这一批客户源,唯品会对用户信息保密异常严格,只有他们自己知道用户信息。我们供应商拿到的全是加密的。

JITX业务说明:因为是由我们直接发货给客户,并且用户信息是保密的,所以承运商+快递单号+快递费 全是由唯品承担。关于退货,这也跟淘宝天猫不一样,商家也不需要处理那些繁杂的退货申请,客户退货也是直接退给唯品的仓库,然后他们处理之后再发回给商家这里。所以 是没有 退货申请 接口的。

PS:关于退货这块,我咨询过公司的业务人员,说是唯品后台会有财务报表,这些报表就是最终生效的订单,我们的业务人员会定期筛选出来导入到我们的ERP系统,这才跟ERP的销售那块的账对上,所以换句话说不用技术人员 再去另外弄销售单。这里不能一概而论所有公司都这样做,还是得看各个公司的业务模式咋样才能决定自家要怎么处理这块。

回调URL:这个就是正式上使用接口必要的参数:access_token,就是需要通过这个获取到。这里要说下,access_token有效期3个月,你只能单独通过https://auth.vip.com/oauth2/authorize?client_id=appKey&response_type=code&redirect_uri=回调URL

这个链接输入VOP系统的账号密码授权之后,唯品会通过你设置的回调地址,将access_token和refresh_token,expires_in,refresh_expires_time返回给你,你需要记录下来,配置到系统中的某个地方,调唯品的接口的时候就用它。那3个月过期之后呢,要么在通过refresh_token(有效期1年)调用唯品的刷新access_token的接口,要么就再一次通过授权这个方式获取token。1年之后吧,就真的需要在一次通过授权去获取access_token了,这就需要你及时更新到才行。

2.拿取 App Key+App Secret

3.配置白名单:即你调用唯品接口的程序发布的服务器IP,

二、码代码

     1.0 先从官方将唯品的sdk下载下来,他们封装好了所有接口的请求(包括model,request),链接

JIT和JITX是可以同时跑的,我是先把JIT的对接好了之后,再去对接JITX的。先上JIT的代码吧,因为JITX的稍微要麻烦点,而且JITX的运行跟商家的运营政策不同而编写规则也不同,毕竟 这是要对接自家的ERP系统的。

PS:这里注意,唯品的接口会时不时来个超时,所以要避免这种情况的发生导致我们错过点拉不到数据或拉不全

1.1 唯品的接口里有自己的一套仓库编码和快递承运商编码,我们使用人家的接口的时候 传递的参数值 也必须是 唯品规定的一套枚举值。所以若是 要和自家的ERP关联起来,就要 配置与 唯品一样的或者能2边关联上的  仓库编码和承运商编码

获取承运商的接口  :vipapis.delivery.JitDeliveryService-1.0.0#getCarrierList

在官网里的这个接口的下面就有仓库的枚举值列表

1.2 先上每个接口必须要用到的公共方法和配置参数:

//这些使用唯品接口必要的参数,我是配置在config里;分别对应 上一节说的 应用的key, 秘钥 ,商户ID ,唯品接口运行地址
private string AppKey = System.Configuration.ConfigurationManager.AppSettings["VPH_AppKey"].ToString();
private string AppSecret = System.Configuration.ConfigurationManager.AppSettings["VPH_AppSecret"].ToString();
private string VendorId = System.Configuration.ConfigurationManager.AppSettings["VPH_VendorId"].ToString();
private string AppUrl = System.Configuration.ConfigurationManager.AppSettings["VPH_AppUrl"].ToString();
 #region 拿Token//令牌信息,token3个月过期,中间一年可通过refresh_token拿取新的token,一年之后就要重新授权private static AccessToken _token;public class AccessToken{public AccessToken(string code, DateTime time, string rcode, DateTime rTime){this.Access_Token = code;this.Time = time;this.Refresh_Token_Time = rTime;this.Refresh_Token = rcode;}public string Access_Token { get; set; }public DateTime Time { get; set; }public string Refresh_Token { get; set; }public DateTime Refresh_Token_Time { get; set; }}private string GetAccessToken(){if (_token == null){var token = System.Configuration.ConfigurationManager.AppSettings["VPH_AccessToken"].ToString();//AccessToken授权拿到的记录在config中var outTime = System.Configuration.ConfigurationManager.AppSettings["VPH_AccessToken_OutTime"].ToString();//AccessToken过期时间授权拿到的记录在config中var reToken = System.Configuration.ConfigurationManager.AppSettings["VPH_ReAccessToken"].ToString();//refresh_token授权拿到的记录在config中var reOutTime = System.Configuration.ConfigurationManager.AppSettings["VPH_ReAccessToken_OutTime"].ToString();//refresh_expires_time授权拿到的记录在config中_token = new AccessToken(token, Convert.ToDateTime(outTime), reToken, Convert.ToDateTime(reOutTime));}var time = _token.Time;if (time != null && time > DateTime.Now.AddDays(1)){return _token.Access_Token;}else if (_token.Refresh_Token_Time > DateTime.Now.AddDays(1)){OauthServiceClient client = new OauthServiceClient();try{ClientInvocationContext instance = new ClientInvocationContext();instance.SetAppKey(AppKey);instance.SetAppSecret(AppSecret);instance.SetAppURL(AppUrl);instance.SetAccessToken(_token.Access_Token);client.SetClientInvocationContext(instance);vipapis.oauth.RefreshTokenRequest request = new vipapis.oauth.RefreshTokenRequest();request.SetRefresh_token(_token.Refresh_Token);request.SetClient_id(AppKey);request.SetClient_secret(AppSecret);var ip = System.Configuration.ConfigurationManager.AppSettings["VPH_WhiteIP"].ToString();//上一节里配置的IP白名单request.SetRequest_client_ip(ip);vipapis.oauth.RefreshTokenResponse token = client.refreshToken(request);_token.Access_Token = token.GetAccess_token();_token.Time = (DateTime)token.GetExpires_time();_token.Refresh_Token = token.GetRefresh_token();_token.Refresh_Token_Time = (DateTime)token.GetRefresh_expires_time();return _token.Access_Token;}catch (OspException e){//LogHelper.WriteLog(new Exception("Token拿取失败:" + "错误编号:" + e.GetReturnCode() + " 返回信息:" + e.GetReturnMessage() + " 系统错误信息:" + e.Message + " 最近一次调用的sign:" + (client.GetClientInvocationContext() == null ? "" : client.GetClientInvocationContext().GetLastInvocation().GetSign())), "GetAccessToken");throw e;}}else{//已过期需要用户手动授权拿到初始化值更新AccessToken的默认值之后才能用//LogHelper.WriteLog(new Exception("Token拿取失败:已完全过期,需要用户手动授权"), "GetAccessToken");throw new OspException("Token已完全过期", new Exception());}}#endregion

1.3  JIT:唯品会会分几个档次下发发货指令(唯品称为:PO单),我们这是每天定时2个点上午和下午,所以我的代码每天在这指令的2个点各跑一次就行。

在唯品接口里执行 【拉取PO单,生成拣货单】2个接口,然后根据当前生成的拣货单 在我们的ERP中生成对应的配货单,即通知我们(商家)的仓库那边,有多少个单需要发什么货。这些事物我在一个接口里写完了

//DBHelper 类,封装好的执行数据库的类,可以自己写,这很简单
//LogHelper 类,封装好的日志记录的类
public HttpResponseMessage VOP_JIT_GetPoList()
{JitDeliveryServiceClient client = new JitDeliveryServiceClient();try{ClientInvocationContext instance = new ClientInvocationContext();instance.SetAppKey(AppKey);instance.SetAppSecret(AppSecret);instance.SetAppURL(AppUrl);instance.SetAccessToken(GetAccessToken());client.SetClientInvocationContext(instance);//获取未拣货订单,然后生成拣货单 int page = 1;while (true){#region 获取PO单,超时时最多执行3次int p_tryTimes = 1;vipapis.delivery.GetPoListResponse unPickList = null;while (true){try{unPickList = client.getPoList(null, null, null, null, null, VendorId, null, null, null, null, page, 100, null, null);break;}catch (OspException e){if (p_tryTimes < 3){p_tryTimes++;continue;}else{//LogHelper.WriteLog(new Exception("3次-拉取PO单失败:" + "错误编号:" + e.GetReturnCode() + " 返回信息:" + e.GetReturnMessage() + " 系统错误信息:" + e.Message + " 最近一次调用的sign:" + (client.GetClientInvocationContext() == null ? "" : client.GetClientInvocationContext().GetLastInvocation().GetSign())), "VOP_JIT_CreatePick");throw e;}}}#endregion//根据拉取的PO单,创建拣货单foreach (var item in unPickList.GetPurchase_order_list()){if (Convert.ToInt32(item.GetNot_pick()) > 0){#region 接口超时时可设置再次尝试执行三次int tryTimes = 1;while (true){try{var re = client.createPick(item.GetPo_no(), Convert.ToInt32(VendorId), null, null, null);break;}catch (OspException e){if (tryTimes < 3){tryTimes++;continue;}else{//LogHelper.WriteLog(new Exception("3次-生成拣货单失败:" + "错误编号:" + e.GetReturnCode() + " 返回信息:" + e.GetReturnMessage() + " 系统错误信息:" + e.Message + " 最近一次调用的sign:" + (client.GetClientInvocationContext() == null ? "" : client.GetClientInvocationContext().GetLastInvocation().GetSign())), "VOP_JIT_CreatePick");throw e;}}}#endregion}}if (unPickList.GetTotal() == null || unPickList.GetTotal() <= (page * 100)){break;}page++;}//获取拣货单列表int page_pick = 1;while (true){#region 拉取现有拣货单,接口超时时可设置再次尝试执行三次vipapis.delivery.GetPickListResponse pickList = null;int tryTimes = 1;while (true){try{pickList = client.getPickList(Convert.ToInt32(VendorId), null, null, null, null, null, null, null, null, null, null, null, null, page_pick, 100, null);break;}catch (OspException e){if (tryTimes < 3){tryTimes++;continue;}else{//LogHelper.WriteLog(new Exception("3次-获取拣货单失败:" + "错误编号:" + e.GetReturnCode() + " 返回信息:" + e.GetReturnMessage() + " 系统错误信息:" + e.Message + " 最近一次调用的sign:" + (client.GetClientInvocationContext() == null ? "" : client.GetClientInvocationContext().GetLastInvocation().GetSign())), "VOP_JIT_CreatePick");throw e;}}}#endregionvar pickData = pickList.GetPicks().Where(w => w.GetDelivery_status() == 0).OrderBy(s => Convert.ToDateTime(s.GetCreate_time()));//0状态为未送货,1已发货foreach (var pick in pickData){//判断ERP是否已配货:VPH_JITNOTICETOPICK,ERP中我自己建好的表,装在JIT订单的记录的表,这里你自己参考就好,不必纠结这里int count1 = Convert.ToInt32(DBHelper.OracleExecuteScalar("select count(0) from VPH_JITNOTICETOPICK where PICKNO = '" + pick.GetPick_no() + "'", DBHelper.ConString));if (count1 > 0){//LogHelper.WriteLog(new Exception("PICK单:" + pick.GetPick_no() + "已存在ERP系统"), "VOP_JIT_GetPoList");continue;}//拿明细,组装数据var resultData = new List<dynamic>();var goodsData = new List<dynamic>();var pickDetail = new List<vipapis.delivery.PickProduct>();var isNormalPick = true;int page_pickDetail = 1;while (true){#region 拉取拣货单明细数据,接口超时时可设置再次尝试执行三次vipapis.delivery.PickDetail detail = null;int p_tryTimes = 1;while (true){try{detail = client.getPickDetail(pick.GetPo_no(), Convert.ToInt32(VendorId), pick.GetPick_no(), page_pickDetail, 100, null);break;}catch (OspException e){if (p_tryTimes < 3){p_tryTimes++;continue;}else{//LogHelper.WriteLog(new Exception("3次-获取拣货单明细失败:" + "错误编号:" + e.GetReturnCode() + " 返回信息:" + e.GetReturnMessage() + " 系统错误信息:" + e.Message + " 最近一次调用的sign:" + (client.GetClientInvocationContext() == null ? "" : client.GetClientInvocationContext().GetLastInvocation().GetSign())), "VOP_JIT_CreatePick");throw;}}}#endregionresultData.AddRange(detail.GetPick_product_list());if (detail.GetTotal() == null || detail.GetTotal() <= (page_pickDetail * 100)){break;}page_pickDetail++;}if (resultData.Count() > 0){//拿取拣货单的明细数据,接下来就是在我们的ERP中进行操作了//1.生成配货单,同时将此单推送给仓库(发货端同事),每个拣货单对应一个配货单,我设计的VPH_JITNOTICETOPICK 表中存的就是 配货单与拣货单的关联数据}}if (pickList.GetTotal() == null || pickList.GetTotal() <= (page_pick * 100)){break;}page_pick++;}return new HttpResponseMessage(){Content = new StringContent(JsonConvert.SerializeObject(new { code = "0", msg = "请求成功!" }), Encoding.UTF8, "application/json"),};}catch (OspException e){//LogHelper.WriteLog(new Exception("拉取PO单生成配单失败:" + "错误编号:" + e.GetReturnCode() + " 返回信息:" + e.GetReturnMessage() + " 系统错误信息:" + e.Message + " 最近一次调用的sign:" + (client.GetClientInvocationContext() == null ? "" : client.GetClientInvocationContext().GetLastInvocation().GetSign())), "VOP_JIT_GetPoList");return new HttpResponseMessage(){Content = new StringContent(JsonConvert.SerializeObject(new { code = "1", msg = "请求失败!" }), Encoding.UTF8, "application/json"),};}
}

JIT的对接我到此就做完了,后面其实还有 发货 要做,但是这里我没有继续做下去了。一因为  JIT的单据一天不会有几个,每个档期一天2个点,我们这只有7个仓库,每个仓库一个拣货单。一天一个档期也就最多14个单。加上 由于货品库存的问题,会经常调整最终 发给唯品的货品清单,所以这里下面就没对接自动发货的接口了。每个公司业务不一样,实现的逻辑和步骤不一样。

1.4  JITX ,这个得分2块来操作,

第一块:获取带寻仓订单->反馈寻仓结果->获取JITX订单拉到ERP中,占库存生成对应的配货单

这一步我这边因为是 单渠道 发货所以没有对接这一块,唯品那边若是单渠道的就会自动给分配好发货仓库。若是需要做多渠道发货的,那可真就有点儿麻烦。下面就是这一块的流程图

 //拉取待寻仓单,确定哪个仓库发货
public ActionResult VOP_JITX_Delivery()
{JitXServiceClient client = new JitXServiceClient();try{ClientInvocationContext instance = new ClientInvocationContext();instance.SetAppKey(AppKey);instance.SetAppSecret(AppSecret);instance.SetAppURL(AppUrl);instance.SetAccessToken(GetAccessToken());client.SetClientInvocationContext(instance);int page = 1;int pageSize = 50;while (true){//获取待寻仓列表var oparam = new vipapis.jitx.GetDeliveryOrdersRequest();oparam.SetVendor_id(Convert.ToInt32(VendorId));DateTime dateStart = new DateTime(1970, 1, 1, 8, 0, 0);oparam.SetStart_time(Convert.ToInt32((DateTime.Now.AddMinutes(-31) - dateStart).TotalSeconds));oparam.SetEnd_time(Convert.ToInt32((DateTime.Now.AddMinutes(-1) - dateStart).TotalSeconds));var status = new List<string>();status.Add("NEW");oparam.SetStatus_list(status);oparam.SetLimit(pageSize);oparam.SetPage(page);#region 接口超时时可设置再次尝试执行三次int tryTimes = 1;vipapis.jitx.GetDeliveryOrdersResponse dOrder = null;while (true){try{dOrder = client.getDeliveryOrders(oparam);break;}catch (OspException e){if (tryTimes < 3){tryTimes++;continue;}else{throw e;}}}#endregion//--------------反馈寻仓结果--------------------var orders = dOrder.GetDelivery_orders();List<vipapis.jitx.FeedbackDeliveryResult> fbkResult = new List<vipapis.jitx.FeedbackDeliveryResult>();//一次只能50个foreach (var o in orders){if (o.GetStatus() == "NEW"){var sendwarehouse = o.GetAvailable_warehouses().FirstOrDefault();//这里要注意,若是你公司是多渠道发货的话,这里可供分配发货的仓库就会有多个,这里的逻辑看自家公司业务上要怎么决定发货制度。我这里默认选择第一个渠道,因为我们这是单渠道的发货if (sendwarehouse == null){//LogHelper.WriteLog(new Exception("唯品JITX寻仓配仓失败:" + o.GetOrder_sn() + "订单没有可分配的仓!"), "VOP_JITX_Delivery");continue;}var fdk = new vipapis.jitx.FeedbackDeliveryResult();fdk.SetOrder_sn(o.GetOrder_sn());fdk.SetFeedback_status("SUCCESS");fdk.SetWarehouse(sendwarehouse);//我们只有一个仓库,那就默认选取第一个fbkResult.Add(fdk);}}var fparam = new vipapis.jitx.FeedbackDeliveryResultRequest();fparam.SetVendor_id(Convert.ToInt32(VendorId));fparam.SetResults(fbkResult);#region 接口超时时可设置再次尝试执行三次int tryTimes_fd = 1;while (true){try{var fdbresult = client.feedbackDeliveryResult(fparam);//获取反馈结果break;}catch (OspException e){if (tryTimes_fd < 3){tryTimes_fd++;continue;}else{throw e;}}}#endregionif (dOrder.GetTotal() == null || dOrder.GetTotal().Value <= (page * pageSize)){break;}page++;}return Json(new { code = 0, msg = "请求成功" });}catch (OspException e){//LogHelper.WriteLog(new Exception("唯品JITX寻仓配仓失败:" + "错误编号:" + e.GetReturnCode() + " 返回信息:" + e.GetReturnMessage() + " 系统错误信息:" + e.Message + " 最近一次调用的sign:" + (client.GetClientInvocationContext() == null ? "" : client.GetClientInvocationContext().GetLastInvocation().GetSign())), "VOP_JITX_Delivery");return Json(new { code = 1, msg = "请求失败" });}finally{if (conn != null) conn.Dispose();}
}//获取JITX订单,拉到ERP中,并生成ERP中的配货单之类的单据,推到仓库那边的系统,他们就能按照这些单据来扫面出库
public HttpResponseMessage VOP_JITX_Order()
{JitXServiceClient client = new JitXServiceClient();try{ClientInvocationContext instance = new ClientInvocationContext();instance.SetAppKey(AppKey);instance.SetAppSecret(AppSecret);instance.SetAppURL(AppUrl);instance.SetAccessToken(GetAccessToken());client.SetClientInvocationContext(instance);var oparam = new vipapis.jitx.GetOrdersRequest();oparam.SetVendor_id(Convert.ToInt32(VendorId));var status = new List<string>();status.Add("10");//未发货  97_10未发已取消oparam.SetOrder_status(status);DateTime dateStart = new DateTime(1970, 1, 1, 8, 0, 0);//唯品的接口要求查询时间间隔不能超过30分钟oparam.SetStart_time(Convert.ToInt32((DateTime.Now.AddMinutes(-31) - dateStart).TotalSeconds));oparam.SetEnd_time(Convert.ToInt32((DateTime.Now.AddMinutes(-1) - dateStart).TotalSeconds));#region 接口超时时可设置再次尝试执行三次int tryTimes = 1;vipapis.jitx.GetOrdersResponse dOrder = null;while (true){try{dOrder = client.getOrders(oparam);break;}catch (OspException e){if (tryTimes < 3){tryTimes++;continue;}else{//LogHelper.WriteLog(new Exception("3次-拉取JITX订单失败:" + "错误编号:" + e.GetReturnCode() + " 返回信息:" + e.GetReturnMessage() + " 系统错误信息:" + e.Message + " 最近一次调用的sign:" + (client.GetClientInvocationContext() == null ? "" : client.GetClientInvocationContext().GetLastInvocation().GetSign())), "VOP_JITX_Order");throw e;}}}#endregion//foreach (var item in dOrder.GetOrders()){//生成配货单,要判断货品是否在ERP中存在与否}return new HttpResponseMessage(){Content = new StringContent(JsonConvert.SerializeObject(new { code = "0", msg = "请求成功!" }), Encoding.UTF8, "application/json"),};}catch (OspException e){//LogHelper.WriteLog(new Exception("唯品JITX拉单配货失败:" + "错误编号:" + e.GetReturnCode() + " 返回信息:" + e.GetReturnMessage() + " 系统错误信息:" + e.Message + " 最近一次调用的sign:" + (client.GetClientInvocationContext() == null ? "" : client.GetClientInvocationContext().GetLastInvocation().GetSign())), "VOP_JITX_Order");return new HttpResponseMessage(){Content = new StringContent(JsonConvert.SerializeObject(new { code = "1", msg = "请求失败!" }), Encoding.UTF8, "application/json"),};}
}

第二块:发货模块

我主要是对接了这一方面的,要实现自动发货,首先程序就要知道哪个订单已经打包发出去了,程序才能通过唯品接口更新唯品那边的订单发货状态。我这边有一套单据回传逻辑,若是仓库那边已扫描出单,就会到ERP这边回传状态回来,然后我就是 定时轮查 那些在ERP中未发货订单状态是不是 已打包发货,然后筛选出来之后 就通过vipapis.jitx.JitXService-1.0.0#ship 接口更新唯品系统上订单的发货状态。

这一块上面说了,邮费是唯品出的,所以面单什么的也必须按照他们的模板打印,最好的最方便发货人员操作的就是 在发货人员一边按单扫面出库的时候,可以同时点打印面单。你可以自己组装模板,通过vipapis.jitx.JitXService-1.0.0#getPrintTemplate这个接口拿取订单的快递字段,我想唯品的人会发你各种快递的模板的。不想自己组装的话那就是用vipapis.jitx.JitXService-1.0.0#getOrderLabel 获取 html面单。

//JITX自动发货
public HttpResponseMessage VOP_JITX_Ship()
{JitXServiceClient client = new JitXServiceClient();try{ClientInvocationContext instance = new ClientInvocationContext();instance.SetAppKey(AppKey);instance.SetAppSecret(AppSecret);instance.SetAppURL(AppUrl);instance.SetAccessToken(GetAccessToken());client.SetClientInvocationContext(instance);//拿取ERP中所有未发货的订单,这里不要照抄啊,每家ERP公司都不一样的数据,我这里只是我这边的情况,程序员搬砖也要会举一反三List<dynamic> listData = DBHelper.OracleExecuteRead(@"", DBHelper.ConString);//第一步:先通过从唯品线上查出这些订单,有没有被客户取消的,若取消了则作废ERP配货单+同时作废仓库系统的对应订单var unsendparam = new vipapis.jitx.GetOrdersByOrderSnRequest();unsendparam.SetVendor_id(Convert.ToInt32(VendorId));var unOds = listData.Where(a => a.SENDGUID == null || a.SENDGUID == "").Select(s => new { ORDERSN = (string)s.ORDERSN, SENDNOTICEGUID = (string)s.SENDNOTICEGUID, SENDNOTICENO = (string)s.SENDNOTICENO }).OrderBy(a => a.ORDERSN);if (unOds.Count() > 0){var times = Math.Floor(unOds.Count() / 500.0);for (int i = 0; i <= times; i++){var os = unOds.Select(a => a.ORDERSN).Skip(500 * i).Take(500).ToList<string>();unsendparam.SetOrder_sns(os);//不能超过500个vipapis.jitx.GetOrdersResponse unsendOrder = null;#region 拉取订单-接口超时时可设置再次尝试执行三次int tryTimes_un = 1;while (true){try{unsendOrder = client.getOrdersByOrderSn(unsendparam);break;}catch (OspException e){if (tryTimes_un < 3){tryTimes_un++;continue;}else{//LogHelper.WriteLog(new Exception("3次-请求getOrdersByOrderSn接口失败:" + "错误编号:" + e.GetReturnCode() + " 返回信息:" + e.GetReturnMessage() + " 系统错误信息:" + e.Message + " 最近一次调用的sign:" + (client.GetClientInvocationContext() == null ? "" : client.GetClientInvocationContext().GetLastInvocation().GetSign())), "VOP_JITX_Ship");break;}}}#endregionif (unsendOrder != null){var canselOrder = unsendOrder.GetOrders().Where(a => a.GetOrder_status() == "97_10").Select(d => d.GetOrder_sn());var canselOrder_T = unOds.Where(a => canselOrder.Contains(a.ORDERSN));foreach (var item in canselOrder_T){//LogHelper.WriteLog(new Exception(item.SENDNOTICENO + " 请求取消订单"), "VOP_JITX_Ship");//作废ERP配货单+同时作废仓库系统的对应订单}}}}//第二步:仓库已配完货的,则更新唯品的发货状态var sendData = listData.Where(a => a.SENDGUID != null && a.SENDGUID != "");foreach (var item in sendData){try{//拉取唯品单据的最新状态var oparam = new vipapis.jitx.GetOrdersByOrderSnRequest();oparam.SetVendor_id(Convert.ToInt32(VendorId));var ods = new List<string>();ods.Add(item.ORDERSN);oparam.SetOrder_sns(ods);vipapis.jitx.GetOrdersResponse dOrder = null;#region 拉取当前订单-接口超时时可设置再次尝试执行三次int tryTimes = 1;while (true){try{dOrder = client.getOrdersByOrderSn(oparam);break;}catch (OspException e){if (tryTimes < 3){tryTimes++;continue;}else{//LogHelper.WriteLog(new Exception("3次-请求getOrdersByOrderSn接口失败:" + "错误编号:" + e.GetReturnCode() + " 返回信息:" + e.GetReturnMessage() + " 系统错误信息:" + e.Message + " 最近一次调用的sign:" + (client.GetClientInvocationContext() == null ? "" : client.GetClientInvocationContext().GetLastInvocation().GetSign())), "VOP_JITX_Ship");break;}}}#endregionif (dOrder == null){continue;}var order = dOrder.GetOrders().FirstOrDefault();if (order.GetOrder_status() == "10")//未发货10,即可进行发货{var sparam = new vipapis.jitx.ShipRequest();sparam.SetVendor_id(Convert.ToInt32(VendorId));var ship = new List<vipapis.jitx.Ship>();var sigelship = new vipapis.jitx.Ship();sigelship.SetOrder_sn(item.ORDERSN);sigelship.SetDelivery_warehouse(order.GetDelivery_warehouse());sigelship.SetTotal_package(1);string detailSql = string.Format(@"", item.SENDGUID);//查询发货单的明细var detailList = DBHelper.OracleExecuteRead(detailSql, DBHelper.ConString);var developer = System.Configuration.ConfigurationManager.AppSettings["VPH_Developer"].ToString();var f360Detail = detailList.Select(a => a.BARCODE + "-" + a.QTY);var vphDetail = order.GetOrder_goods().Select(a => a.GetBarcode() + "-" + a.GetQuantity());if (f360Detail.Count() != vphDetail.Count()){string sql_ = string.Format(@"insert into J_Transfer_Notice(Type,Code,Date,SendUser,Send,SendText,Modify) select '唯品配货通知',replace(newid(), '-', ''),Getdate(),'" + developer + "',0,'{0}',Getdate() ", item.ORDERSN + "唯品JITX发货失败:发货明细与唯品订单明细不一致");DBHelper.ExecuteNonQuery(sql_, DBHelper.BusinessConection);LogHelper.WriteLog(new Exception(item.ORDERSN + "唯品JITX发货失败:发货明细与唯品订单明细不一致"), "VOP_JITX_Ship");continue;}var verfityData = f360Detail.Union(vphDetail);if (verfityData.Count() != vphDetail.Count()){string sql_ = string.Format(@"insert into J_Transfer_Notice(Type,Code,Date,SendUser,Send,SendText,Modify) select '唯品配货通知',replace(newid(), '-', ''),Getdate(),'" + developer + "',0,'{0}',Getdate() ", item.ORDERSN + "唯品JITX发货失败:发货明细与唯品订单明细不一致");DBHelper.ExecuteNonQuery(sql_, DBHelper.BusinessConection);LogHelper.WriteLog(new Exception(item.ORDERSN + "唯品JITX发货失败:发货明细与唯品订单明细不一致"), "VOP_JITX_Ship");continue;}#endregionvar detail = new List<vipapis.jitx.Package>();var p = new vipapis.jitx.Package();p.SetBox_no(1);DateTime dateStart = new DateTime(1970, 1, 1, 8, 0, 0);p.SetOqc_date(Convert.ToInt32((Convert.ToDateTime(item.CREATEDDATE) - dateStart).TotalSeconds));p.SetTransport_no(order.GetTransport_no());p.SetPackage_no(item.SENDSHEETID);List<vipapis.jitx.PackageDetail> goods = detailList.Select(a =>{var temp = new vipapis.jitx.PackageDetail();temp.SetBarcode(a.BARCODE);temp.SetQuantity((int)a.QTY);return temp;}).ToList<vipapis.jitx.PackageDetail>();p.SetDetails(goods);detail.Add(p);sigelship.SetPackages(detail);ship.Add(sigelship);sparam.SetShips(ship);vipapis.jitx.ShipResponse re = null;#region 发货-接口超时时可设置再次尝试执行三次int tryTimes_ship = 1;while (true){try{re = client.ship(sparam);break;}catch (OspException e){if (tryTimes_ship < 3){tryTimes_ship++;continue;}else{//LogHelper.WriteLog(new Exception("3次-请求ship接口失败:" + "错误编号:" + e.GetReturnCode() + " 返回信息:" + e.GetReturnMessage() + " 系统错误信息:" + e.Message + " 最近一次调用的sign:" + (client.GetClientInvocationContext() == null ? "" : client.GetClientInvocationContext().GetLastInvocation().GetSign())), "VOP_JITX_Ship");break;}}}#endregionif (re == null){continue;}if (re.GetSuccess_num() > 0){//发货成功,则更新ERP对应订单的状态}else{string error = "唯品订单号:" + item.ORDERSN + " 发货单号:" + item.SENDSHEETID + " 更新唯品订单发货状态失败:" + re.GetFailed_list().FirstOrDefault().GetMsg();//LogHelper.WriteLog(new Exception("唯品JITX发货失败:" + error), "VOP_JITX_Ship");}}else if (order.GetOrder_status() == "97_10")//未发取消,已配完货客户取消{//这里上面也说了,其实要是已出库了,就只能在重新做入库了,这个得由发货人员去手动入单,因为实物在他们那边,系统若是做自动入库,很容易造成库存差异}}catch (Exception ex){string error = "唯品订单号:" + item.ORDERSN + " 发货单号:" + item.SENDSHEETID + " 错误信息:" + ex.Message;//LogHelper.WriteLog(new Exception("唯品JITX发货失败:" + error), "VOP_JITX_Ship");continue;}}return new HttpResponseMessage(){Content = new StringContent(JsonConvert.SerializeObject(new { code = "0", msg = "请求成功!" }), Encoding.UTF8, "application/json"),};}catch (OspException e){//LogHelper.WriteLog(new Exception("唯品JITX发货失败:" + "错误编号:" + e.GetReturnCode() + " 返回信息:" + e.GetReturnMessage() + " 系统错误信息:" + e.Message + " 最近一次调用的sign:" + (client.GetClientInvocationContext() == null ? "" : client.GetClientInvocationContext().GetLastInvocation().GetSign())), "VOP_JITX_Ship");return new HttpResponseMessage(){Content = new StringContent(JsonConvert.SerializeObject(new { code = "1", msg = "请求失败!" }), Encoding.UTF8, "application/json"),};}
}

到此,唯品的JIT和JITX都对接完了,再次申明:关于与ERP方面的对接仅限于参考,我提供我这边的实际情况对接出来的经验与思路。同志们你们要自己做自家公司的对接请按实际情况修改代码。

总结:其实使用唯品的接口还是异常简单的,真的简单,我这是从没在唯品上买过东西的人,有时候会把淘宝的逻辑套在他们身上想,发现很多时候都会想多了,毕竟是2个不同的平台。先试着问唯品的技术人员,然后再来自己实际尝试,才会得出能解释的答案,也不要一不懂就问,先自己尝试之后再去问,毕竟人家也很忙,若是发现自己问了个弱智白痴问题,那就尴尬了。

以上纯属个人独自研究成果,仅供参考,转载请注明出处

C# 唯品会JITJITX对接相关推荐

  1. 消费金融业务模式结构图

    1.商城模式 1.1.模式简介: 用户通过线上商城购买商品,为消费分期的一种常见模式.消费金融公司提供金融服务费,商家获取周转资金,用户获取需求商品.消费金融公司也可以直接和商家合作,直接给用户提供商 ...

  2. 唯品会实时计算平台的演进之路

    来自:DBAplus社群 本文根据王新春老师在[2018 DAMS中国数据资产管理峰会]现场演讲内容整理而成. 讲师介绍 王新春,唯品会高级经理.数据平台实时团队高级架构师,主要负责实时计算平台.实时 ...

  3. Mercury:唯品会全链路应用监控系统解决方案详解

    姚捷,唯品会平台架构部高级架构师,加入唯品会前有超过 10 年的金融/保险互联网技术架构和团队管理经验,擅长以产品思维设计和构建系统.现专注于互联网基础架构,负责唯品会全链路监控/分析平台的开发,管理 ...

  4. 唯品会订单分库分表的实践总结以及关键步骤--转

    原文地址:http://www.infoq.com/cn/articles/summary-and-key-steps-of-vip-orders-depots-table 随着唯品会业务的快速发展, ...

  5. 唯品会在 Flink 容器化与平台化上的建设实践

    简介:唯品会 Flink 的容器化实践应用,Flink SQL 平台化建设,以及在实时数仓和实验平台上的应用案例. 转自dbaplus社群公众号 作者:王康,唯品会数据平台高级开发工程师 GitHub ...

  6. 唯品会:在 Flink 容器化与平台化上的建设实践

    简介: 唯品会 Flink 的容器化实践应用,Flink SQL 平台化建设,以及在实时数仓和实验平台上的应用案例. 转自dbaplus社群公众号 作者:王康,唯品会数据平台高级开发工程师 自 201 ...

  7. 抓住六个点,谈唯品会的峰值系统应对实践

    区别于其他网购品牌唯品会定位是"一家专门做特卖的网站", 商业模式为"名牌折扣+限时抢购+正品保险",即"闪购"(flash sales)模 ...

  8. 构建与定制:唯品会PaaS基于Kubernetes的实践

    主要工作内容包括:平台DevOps方案流程优化,持续部署,平台日志收集,Docker以及Kubernetes研究. 大家好,我是唯品会PaaS团队的王成昌,与大家分享一下PaaS在Kubernetes ...

  9. 唯品会的服务化[转]

    2019独角兽企业重金招聘Python工程师标准>>> http://www.infoq.com/cn/articles/vip-11-11-peak-system-practice ...

最新文章

  1. windows怎么下载安装python-windows下numpy下载与安装图文教程
  2. C语言博客作业--嵌套循环
  3. WINCE基于AT050TN22屏的时序描述
  4. php 安装redis php扩展
  5. android实现自动横竖屏切换,android 中实现横竖屏切换【原创】
  6. 一般市区有测速吗_高速公路增加这么多龙门架,有抓拍超速违章功能吗?可要仔细辨别...
  7. matlab monte carlo,Monte Carlo Simulation
  8. web应用转化为saas多租户
  9. .Net语言 APP开发平台——Smobiler学习日志:用Gridview控件设计较复杂的表单
  10. 短视频解析技术原理,去水印原理分析整理汇总
  11. win7注册表无法修改计算机名称,win7注册表无法修改怎么办 win7注册表不能更改怎么办...
  12. HTC G10官方解锁 刷机
  13. 软件企业 选择CodeMeter的十大理由
  14. 2007年日历带农历表_2007年日历表,2007年农历表(阴历阳历节日对照表)
  15. 利用ansys计算机械结构最小安全系数教程,安全系数
  16. Touchgfx - 4 - Bitmap Cache
  17. 前端—react项目及版本更新对比
  18. uniapp提示用户开启定位,跳转到开启定位页面
  19. 大数据管理平台(一)概述
  20. Google Chrome 63正式版下载

热门文章

  1. 7.1.5、Sqoop__sqoop常用命令参数,import,export,hive,数据库连接,hbase等
  2. 微信小程序详细图文教程-10分钟完成微信小程序开发部署发布 小程序趟过的坑,你遇到几个??
  3. Linux下无法识别Intel Dual Band Wireless-AC 3165模块
  4. 12米数字高程DEM现已上线!附DEM专题图制作教程
  5. 03 SpringMVC 处理响应
  6. IOS 设置icon 出现的bug The app icon set named “AppIcon“ did not have any applicable content.
  7. 听声音做钥匙?!慢放开锁音轨,黑客就能破解常用门锁
  8. ryu与mininet的连接
  9. 诗歌二 未知来生相见否,陌上逢却在少年。
  10. doxygen简单介绍