渠道订单设计

  • 目的
      我们是提供分销服务的,一般的供销平台提供了代销和经销两种线上经营模式

代销:分销商无需拿货就可以直接销售并赚取差价,由供应商一件代发

经销:分销商直接从供应商处批量采购并囤货销售,享受更多折扣

1)     代销基本业务流程

渠道订单设计

2)     经销基本业务流程

渠道订单设计
 
而我们的系统是提供代销服务。就是分销商在其他的平台例如淘宝销售商品,生成订单反馈给我们系统,我们系统需要把外部订单转换成我们自己的订单,
生成和他对应的供应商订单,然后进入我们的系统的订单流程,最终由我们的仓储平台发货。因此,我们需要获取不同渠道的订单, 转换成我们系统的的订单。
 
  • 条件
      1. 要获取不同渠道的订单,就要涉及到对方的开放平台(API) 。因此,首先要解决如何对接开放平台?对接开放平台一般都需要用户授权,授权流程遵循oauth2.0协议。
      例如,如果您的应用和淘宝开放平台对接后,需要获取用户隐私信息(如:商品、订单、收藏夹等),为保证用户数据的安全性与隐私性,您的应用需要取得用户的授权。在这种情况下,您的应用需要引导用户完成“使用淘宝帐号登录并授权”的流程。 淘宝的Taobao ID(淘帐号)产品,采用国际通用的OAuth2.0标准协议,作为用户身份验证与授   权协议,支持网站、手机客户端、桌面客户端。
      淘宝的授权流程需要您有自己的web服务器,能够保存应用本身的密钥以及状态,可以通过https直接访问淘宝的授权服务器。
授权过程分为两个步骤:
  渠道订单设计
 
上面的淘宝的授权流程就遵循oauth2.0协议。
 
    2. 授权成功后,还需要通过调用api获取所属信息。api调用需要遵循对方的要求。例如淘宝的api调用方式

TOP 作为淘宝数据插槽,只要用户按照TOP的规范拼装一个正确的URL,通过HTTP请求到TOP,就能够拿到用户自己需要的数据。调用原理示意图如下(注: 淘宝 API <接口>采用 REST 风格,只需将所需参数拼装成http请求,即可调用。故支持 http 协议请求的程序语言,均可调用淘宝API,例如php、C#、asp、java、delphi 等。)

渠道订单设计
  3. 获取了数据,还需要把数据转换成我们的自己的数据。

总的大体流程如下:
 
渠道订单设计
 
  • 实现
     针对上面的分析,我们需要实现oauth授权,api调用,csv导入和导出。
 
      1.oauth授权
              我们仅仅是oauth的web的client实现,虽然目前有很多流行的框架.例如oauth官方推荐的
     调查了spring social,实现的方式很好,代理拦截+template模块化获取资源, 只是目前还不支持top,虽然已支持facebook, twitter,github等,如果自己实现工作量有点大。
   spring security,还需要引入spring security这个框架,虽然先前我使用过,有点杀鸡焉用牛刀的感觉
   Restlet是一个rest框架,和spring的实现有重叠。
 
  最终选择了apache的实现,参照demo很容易实现,授权的流程。
@RequestMapping ("/authorize/{providerId}" )
    public ModelAndView authorize( @PathVariable
    String providerId, Model model) throws OAuthSystemException, IOException {
        try {

            OauthTemplate oauth = getTemplate(providerId);
            OauthProviderConfig appOauth = oauth.getProviderConfig();

            OAuthClientRequest request = OAuthClientRequest.authorizationLocation(appOauth.getAuthzEndpoint()).setClientId(appOauth.getClientId()).setRedirectURI(appOauth.getRedirectUri())
                    .setResponseType(ResponseType.CODE .toString()).setScope(appOauth.getScope()).setState(appOauth.getState()).buildQueryMessage();

            return new ModelAndView(new RedirectView(request.getLocationUri()));
        } catch (Exception e) {
            model.addAttribute( "errorMsg" , "授权失败" );
            logger.info( "authorize" , e);
            return new ModelAndView("redirect:/oauthClient/list.htm");
        }
    }

    @RequestMapping (value = "/token/{providerId}" , method = RequestMethod.GET)
    public ModelAndView handleRedirect( @PathVariable
    String providerId, Model model, HttpServletRequest req) throws OAuthSystemException {

        try {

            OauthTemplate oauth = getTemplate(providerId);
            OauthProviderConfig appOauth = oauth.getProviderConfig();

            // Create the response wrapper
            OAuthAuthzResponse oar = null ;
            oar = OAuthAuthzResponse. oauthCodeAuthzResponse(req);

            // Get Authorization Code
            String code = oar.getCode();

            OAuthClientRequest request = OAuthClientRequest.tokenLocation(appOauth.getTokenEndpoint()).setClientId(appOauth.getClientId()).setClientSecret(appOauth.getClientSecret())
                    .setRedirectURI(appOauth.getRedirectUri()).setCode(code).setGrantType(GrantType.AUTHORIZATION_CODE).buildBodyMessage();

            OAuthClient client = new OAuthClient(SSL_CLIENT);

            OAuthJSONAccessTokenResponse oauthResponse = client.accessToken(request, OAuthJSONAccessTokenResponse.class);

            Boolean oauthResult = channelService .saveInfo(oauth.convertResponse(oauthResponse));
            model.addAttribute( "successFlag" , oauthResult);

        } catch (OAuthProblemException e) {
            logger.info( "oauth" , e);
            model.addAttribute( "errorMsg" , "授权失败" );
        }

        return new ModelAndView("redirect:/oauthClient/list.htm");

    }
 
 
  2. 授权完成了,需要api调用,选择了top官方推荐的sdk模式,针对以后可能不同的渠道,例如还需要支持paipai,采用了模板模式
  获取模板如
private OauthTemplate getTemplate(String appId) throws OAuthSystemException {
        OauthTemplate oauth = null ;
        for (OauthTemplate template : oauthTemplates ) {
            if (template.isProvider(appId)) {
                oauth = template;
                break ;
            }
        }
        if (oauth == null) {
            throw new OAuthSystemException("无效的用户请求" );
        }
        return oauth;
    }
 
 
 
 针对获取不同的资源,采取了命令模式,同时可以实现session token的拦截验证,目前,由于授权的限制,仅仅简单判定一下是否报错就可了。
 
 
public Map<String, Object> doAuthInfo(Integer cid, AuthInfo authInfo) throws OAuthSystemException {
        ChannelInfo info = channelService .getInfo(cid);
        if (info == null) {
            throw new OAuthSystemException("无效的请求 ");
        }
        Map<String, Object> resultMap = new HashMap<String, Object>();
        resultMap.put( "flag" , Boolean.TRUE);
        try {
            OauthTemplate oauth = getTemplate(info.getProviderId());

            authInfo.doExecute(info, oauth, resultMap);

        } catch (OAuthProblemException e) {
            logger.info( "授权错误" , e);
            resultMap.put( "flag" , Boolean.FALSE);
            resultMap.put( "redirect" , MessageFormat.format( "authorize/{0}.htm" , info.getProviderId()));
        } catch (Exception e) {
            logger.info( "请求资源错误" , e);
            resultMap.put( "flag" , Boolean.FALSE);
        }
        return resultMap;
    }
 
 3.csv实现
   采用第三方opencsv实现的。
  
 
  • 结论
     
    总的原则,还是面向对象的思想,解耦。

相关推荐