C# 唯品会JITJITX对接
前言:其实唯品会的接口使用还是非常简单,开发者可能有疑问还是他们的接口流程问题和如何与自家的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.模式简介: 用户通过线上商城购买商品,为消费分期的一种常见模式.消费金融公司提供金融服务费,商家获取周转资金,用户获取需求商品.消费金融公司也可以直接和商家合作,直接给用户提供商 ...
- 唯品会实时计算平台的演进之路
来自:DBAplus社群 本文根据王新春老师在[2018 DAMS中国数据资产管理峰会]现场演讲内容整理而成. 讲师介绍 王新春,唯品会高级经理.数据平台实时团队高级架构师,主要负责实时计算平台.实时 ...
- Mercury:唯品会全链路应用监控系统解决方案详解
姚捷,唯品会平台架构部高级架构师,加入唯品会前有超过 10 年的金融/保险互联网技术架构和团队管理经验,擅长以产品思维设计和构建系统.现专注于互联网基础架构,负责唯品会全链路监控/分析平台的开发,管理 ...
- 唯品会订单分库分表的实践总结以及关键步骤--转
原文地址:http://www.infoq.com/cn/articles/summary-and-key-steps-of-vip-orders-depots-table 随着唯品会业务的快速发展, ...
- 唯品会在 Flink 容器化与平台化上的建设实践
简介:唯品会 Flink 的容器化实践应用,Flink SQL 平台化建设,以及在实时数仓和实验平台上的应用案例. 转自dbaplus社群公众号 作者:王康,唯品会数据平台高级开发工程师 GitHub ...
- 唯品会:在 Flink 容器化与平台化上的建设实践
简介: 唯品会 Flink 的容器化实践应用,Flink SQL 平台化建设,以及在实时数仓和实验平台上的应用案例. 转自dbaplus社群公众号 作者:王康,唯品会数据平台高级开发工程师 自 201 ...
- 抓住六个点,谈唯品会的峰值系统应对实践
区别于其他网购品牌唯品会定位是"一家专门做特卖的网站", 商业模式为"名牌折扣+限时抢购+正品保险",即"闪购"(flash sales)模 ...
- 构建与定制:唯品会PaaS基于Kubernetes的实践
主要工作内容包括:平台DevOps方案流程优化,持续部署,平台日志收集,Docker以及Kubernetes研究. 大家好,我是唯品会PaaS团队的王成昌,与大家分享一下PaaS在Kubernetes ...
- 唯品会的服务化[转]
2019独角兽企业重金招聘Python工程师标准>>> http://www.infoq.com/cn/articles/vip-11-11-peak-system-practice ...
最新文章
- windows怎么下载安装python-windows下numpy下载与安装图文教程
- C语言博客作业--嵌套循环
- WINCE基于AT050TN22屏的时序描述
- php 安装redis php扩展
- android实现自动横竖屏切换,android 中实现横竖屏切换【原创】
- 一般市区有测速吗_高速公路增加这么多龙门架,有抓拍超速违章功能吗?可要仔细辨别...
- matlab monte carlo,Monte Carlo Simulation
- web应用转化为saas多租户
- .Net语言 APP开发平台——Smobiler学习日志:用Gridview控件设计较复杂的表单
- 短视频解析技术原理,去水印原理分析整理汇总
- win7注册表无法修改计算机名称,win7注册表无法修改怎么办 win7注册表不能更改怎么办...
- HTC G10官方解锁 刷机
- 软件企业 选择CodeMeter的十大理由
- 2007年日历带农历表_2007年日历表,2007年农历表(阴历阳历节日对照表)
- 利用ansys计算机械结构最小安全系数教程,安全系数
- Touchgfx - 4 - Bitmap Cache
- 前端—react项目及版本更新对比
- uniapp提示用户开启定位,跳转到开启定位页面
- 大数据管理平台(一)概述
- Google Chrome 63正式版下载
热门文章
- 7.1.5、Sqoop__sqoop常用命令参数,import,export,hive,数据库连接,hbase等
- 微信小程序详细图文教程-10分钟完成微信小程序开发部署发布 小程序趟过的坑,你遇到几个??
- Linux下无法识别Intel Dual Band Wireless-AC 3165模块
- 12米数字高程DEM现已上线!附DEM专题图制作教程
- 03 SpringMVC 处理响应
- IOS 设置icon 出现的bug The app icon set named “AppIcon“ did not have any applicable content.
- 听声音做钥匙?!慢放开锁音轨,黑客就能破解常用门锁
- ryu与mininet的连接
- 诗歌二 未知来生相见否,陌上逢却在少年。
- doxygen简单介绍