- 目的
我们是提供分销服务的,一般的供销平台提供了代销和经销两种线上经营模式
代销:分销商无需拿货就可以直接销售并赚取差价,由供应商一件代发
经销:分销商直接从供应商处批量采购并囤货销售,享受更多折扣
1) 代销基本业务流程
2) 经销基本业务流程
![](https://i-blog.csdnimg.cn/blog_migrate/f3b272f984904800a41bdf4522663421.jpeg)
而我们的系统是提供代销服务。就是分销商在其他的平台例如淘宝销售商品,生成订单反馈给我们系统,我们系统需要把外部订单转换成我们自己的订单,
生成和他对应的供应商订单,然后进入我们的系统的订单流程,最终由我们的仓储平台发货。因此,我们需要获取不同渠道的订单, 转换成我们系统的的订单。
- 条件
1. 要获取不同渠道的订单,就要涉及到对方的开放平台(API) 。因此,首先要解决如何对接开放平台?对接开放平台一般都需要用户授权,授权流程遵循oauth2.0协议。
例如,如果您的应用和淘宝开放平台对接后,需要获取用户隐私信息(如:商品、订单、收藏夹等),为保证用户数据的安全性与隐私性,您的应用需要取得用户的授权。在这种情况下,您的应用需要引导用户完成“使用淘宝帐号登录并授权”的流程。 淘宝的Taobao ID(淘帐号)产品,采用国际通用的OAuth2.0标准协议,作为用户身份验证与授 权协议,支持网站、手机客户端、桌面客户端。
淘宝的授权流程需要您有自己的web服务器,能够保存应用本身的密钥以及状态,可以通过https直接访问淘宝的授权服务器。
授权过程分为两个步骤:
![](https://i-blog.csdnimg.cn/blog_migrate/fcfc731426ab884397c6803c19f12e48.png)
上面的淘宝的授权流程就遵循oauth2.0协议。
2. 授权成功后,还需要通过调用api获取所属信息。api调用需要遵循对方的要求。例如淘宝的api调用方式
TOP 作为淘宝数据插槽,只要用户按照TOP的规范拼装一个正确的URL,通过HTTP请求到TOP,就能够拿到用户自己需要的数据。调用原理示意图如下(注: 淘宝 API <接口>采用 REST 风格,只需将所需参数拼装成http请求,即可调用。故支持 http 协议请求的程序语言,均可调用淘宝API,例如php、C#、asp、java、delphi 等。)
![](https://i-blog.csdnimg.cn/blog_migrate/7621ce781163cb00f5f2d1a15c946bd8.jpeg)
3. 获取了数据,还需要把数据转换成我们的自己的数据。
总的大体流程如下:
![](https://i-blog.csdnimg.cn/blog_migrate/b63b752e84c914d8abc43835e48d599f.jpeg)
- 实现
针对上面的分析,我们需要实现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实现的。
- 结论
总的原则,还是面向对象的思想,解耦。