diff --git a/mallinkService/src/main/java/com/iformall/douyin/pay/DouYinPayHelper.java b/mallinkService/src/main/java/com/iformall/douyin/pay/DouYinPayHelper.java index 27772b80d..d8f284dce 100644 --- a/mallinkService/src/main/java/com/iformall/douyin/pay/DouYinPayHelper.java +++ b/mallinkService/src/main/java/com/iformall/douyin/pay/DouYinPayHelper.java @@ -21,6 +21,7 @@ import com.iformall.douyin.pay.orderQuery.OrderQueryResult; import com.iformall.utils.HttpUtil; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import springfox.documentation.spring.web.json.Json; /** @@ -193,7 +194,9 @@ public class DouYinPayHelper { return EnumPayStatus.PAY_STATUS_ORDER_NOT_EXISTS.getCode(); } String orderStatus = result.getOrderStatus(); - if("PROCESSING".equals(orderStatus)){//以观后效 + if(StringUtils.isBlank(orderStatus)){ + return EnumPayStatus.PAY_STATUS_ORDER_NOT_EXISTS.getCode(); + }else if("PROCESSING".equals(orderStatus)){//以观后效 return EnumPayStatus.PAY_STATUS_NOTPAY.getCode(); }else if("SUCCESS".equals(orderStatus)){ return EnumPayStatus.PAY_STATUS_SUCCESS.getCode(); diff --git a/mallinkService/src/main/java/com/iformall/douyin/web/api/TtWebGetRequestExecutor.java b/mallinkService/src/main/java/com/iformall/douyin/web/api/TtWebGetRequestExecutor.java new file mode 100644 index 000000000..1dca9841a --- /dev/null +++ b/mallinkService/src/main/java/com/iformall/douyin/web/api/TtWebGetRequestExecutor.java @@ -0,0 +1,98 @@ +package com.iformall.douyin.web.api; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import me.chanjar.weixin.common.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.RequestExecutor; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.ResponseHandler; +import me.chanjar.weixin.common.util.http.apache.Utf8ResponseHandler; +import org.apache.commons.lang3.StringUtils; +import org.apache.http.HttpHost; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; + +import java.io.IOException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * . + * + * @author ecoolper + * @date 2017/5/4 + */ +public class TtWebGetRequestExecutor implements RequestExecutor { + protected RequestHttp requestHttp; + + public TtWebGetRequestExecutor(RequestHttp requestHttp) { + this.requestHttp = requestHttp; + } + + @Override + public void execute(String uri, String data, ResponseHandler handler, WxType wxType) throws WxErrorException, IOException { + handler.handle(this.execute(uri, data, wxType)); + } + + @Override + public String execute(String uri, String queryParam, WxType wxType) throws WxErrorException, IOException { + String access_token = handleUrl(uri); + if(StringUtils.isBlank(access_token)){ + WxError wxError = WxError.fromJson("{\n" + + " \"description\": \"未找到access_token\",\n" + + " \"error_code\": -1\n" + + " }"); + throw new WxErrorException(wxError); + } + + if (queryParam != null) { + if (uri.indexOf('?') == -1) { + uri += '?'; + } + uri += uri.endsWith("?") ? queryParam : '&' + queryParam; + } + + HttpGet httpGet = new HttpGet(uri); + httpGet.addHeader("Content-Type","application/json"); + httpGet.addHeader("access-token",access_token); + if (requestHttp.getRequestHttpProxy() != null) { + RequestConfig config = RequestConfig.custom().setProxy(requestHttp.getRequestHttpProxy()).build(); + httpGet.setConfig(config); + } + + try (CloseableHttpResponse response = requestHttp.getRequestHttpClient().execute(httpGet)) { + String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); + JSONObject jsonObject = JSON.parseObject(responseContent); + JSONObject data = jsonObject.getJSONObject("data"); + WxError error = WxError.fromJson(data.toJSONString()); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } + return data.toString(); + } finally { + httpGet.releaseConnection(); + } + } + + //去除链接中access_token 并返回 + private String handleUrl(String url){ + String access_token = null; + Pattern pXM = Pattern.compile("access_token=([^&]*)"); + Matcher mXM = pXM.matcher(url); + int i = 0; + while (mXM.find()) { + i++; + access_token = mXM.group(1); + } + if(i > 1){ + return null; + } + url = url.replaceAll("&?access_token=[^&]*",""); + return access_token; + } + +} diff --git a/mallinkService/src/main/java/com/iformall/douyin/web/api/TtWebPostRequestExecutor.java b/mallinkService/src/main/java/com/iformall/douyin/web/api/TtWebPostRequestExecutor.java new file mode 100644 index 000000000..53acf6e64 --- /dev/null +++ b/mallinkService/src/main/java/com/iformall/douyin/web/api/TtWebPostRequestExecutor.java @@ -0,0 +1,109 @@ +package com.iformall.douyin.web.api; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import me.chanjar.weixin.common.WxType; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.RequestExecutor; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.ResponseHandler; +import me.chanjar.weixin.common.util.http.apache.Utf8ResponseHandler; +import org.apache.commons.lang3.StringUtils; +import org.apache.http.Consts; +import org.apache.http.HttpHost; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; + +import java.io.IOException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * . + * + * @author ecoolper + * @date 2017/5/4 + */ +public class TtWebPostRequestExecutor implements RequestExecutor { + protected RequestHttp requestHttp; + + public TtWebPostRequestExecutor(RequestHttp requestHttp) { + this.requestHttp = requestHttp; + } + + @Override + public void execute(String uri, String data, ResponseHandler handler, WxType wxType) + throws WxErrorException, IOException { + handler.handle(this.execute(uri, data, wxType)); + } + + @Override + public String execute(String uri, String postEntity, WxType wxType) throws WxErrorException, IOException { + + String access_token = handleUrl(uri); + if(StringUtils.isBlank(access_token)){ + WxError wxError = WxError.fromJson("{\n" + + " \"description\": \"未找到access_token\",\n" + + " \"error_code\": -1\n" + + " }"); + throw new WxErrorException(wxError); + } + + HttpPost httpPost = new HttpPost(uri); + httpPost.addHeader("Content-Type","application/json"); + httpPost.addHeader("access-token",access_token); + + if (requestHttp.getRequestHttpProxy() != null) { + RequestConfig config = RequestConfig.custom().setProxy(requestHttp.getRequestHttpProxy()).build(); + httpPost.setConfig(config); + } + + if (postEntity != null) { + StringEntity entity = new StringEntity(postEntity, Consts.UTF_8); + entity.setContentType("application/json; charset=utf-8"); + httpPost.setEntity(entity); + } + + try (CloseableHttpResponse response = requestHttp.getRequestHttpClient().execute(httpPost)) { + String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); + if (responseContent.isEmpty()) { + throw new WxErrorException(WxError.builder().errorCode(9999).errorMsg("无响应内容").build()); + } + + if (responseContent.startsWith("")) { + //xml格式输出直接返回 + return responseContent; + } + JSONObject jsonObject = JSON.parseObject(responseContent); + JSONObject data = jsonObject.getJSONObject("data"); + WxError error = WxError.fromJson(data.toJSONString()); + if (error.getErrorCode() != 0) { + throw new WxErrorException(error); + } + return data.toString(); + } finally { + httpPost.releaseConnection(); + } + } + + //去除链接中access_token 并返回 + private String handleUrl(String url){ + String access_token = null; + Pattern pXM = Pattern.compile("access_token=([^&]*)"); + Matcher mXM = pXM.matcher(url); + int i = 0; + while (mXM.find()) { + i++; + access_token = mXM.group(1); + } + if(i > 1){ + return null; + } + url = url.replaceAll("&?access_token=[^&]*",""); + return access_token; + } +} diff --git a/mallinkService/src/main/java/com/iformall/douyin/web/api/TtWebProductService.java b/mallinkService/src/main/java/com/iformall/douyin/web/api/TtWebProductService.java new file mode 100644 index 000000000..56d8b6b1f --- /dev/null +++ b/mallinkService/src/main/java/com/iformall/douyin/web/api/TtWebProductService.java @@ -0,0 +1,97 @@ +package com.iformall.douyin.web.api; + + +import com.iformall.douyin.web.bean.*; +import me.chanjar.weixin.common.error.WxErrorException; + +/** + * 商铺 商品poi接入操作接口. + * + * @author + */ +public interface TtWebProductService { + + /** + *商铺同步 + */ + String POST_SUPPLIER_SYNC = "/poi/supplier/sync/"; + /** + *查询店铺 + */ + String GET_SUPPLIER_QUERY = "/poi/supplier/query/"; + /** + * 商品同步 + *多门店SPU同步 + */ + String POST_SPU_SYNC = " /poi/v2/spu/sync/"; + /** + * 商品状态同步 + *多门店SPU状态同步 + */ + String POST_SPU_STATUS_SYNC = "/poi/v2/spu/status_sync/"; + /** + * 商品信息查询 + *多门店SPU信息查询 + */ + String GET_SPU_GET = "/poi/v2/spu/get/"; + /** + * 商品库存 + *多门店SPU信息查询 + */ + String POST_SPU_STOCK_UPDATE = "/poi/v2/spu/stock_update/"; + + /** + * 商铺接入 + * https://open.douyin.com/platform/doc?doc=docs/openapi/life-service-open-ability/shop/synchronism + * 商铺接入之前,必须要先进行POI匹配,只有POI匹配成功的商铺才能进行商品接入 + * 在spu 商品同步前,必须进行商铺同步 + * 商铺同步不需要人工审核 + * return supplier_id 抖音平台商户ID + */ + String supplierSync(TtSupplierSync supplierSync) throws WxErrorException; + + /** + * 查询店铺 + * https://open.douyin.com/platform/doc?doc=docs/openapi/life-service-open-ability/shop/search + * + * @param supplierExtId + * supplier_ext_id 第三方上传任务id列表,多个任务id用 , 分割,单次查询最多10个任务。 + */ + TtSupplierSyncQuery supplierQuery(String supplierExtId) throws WxErrorException ; + + /** + * 多门店SPU同步 + * https://open.douyin.com/platform/doc?doc=docs/openapi/life-service-open-ability/goods-repo/spu-sync + * + * + * @return .spu_id 抖音平台SPU ID + */ + String spuSync(TtSpuSync spuSync) throws WxErrorException ; + + /** + * 多门店SPU状态同步 + * https://open.douyin.com/platform/doc?doc=docs/openapi/life-service-open-ability/goods-repo/spu-status-sync + * + * + * @return .spu_ext_id_list 抖音平台SPU ID + */ + TtSpuStatusSync spuStatusSync(TtSpuStatusSync spuStatusSync) throws WxErrorException ; + + /** + * 多门店SPU信息查询 + * https://open.douyin.com/platform/doc?doc=docs/openapi/life-service-open-ability/goods-repo/spu-info-query + * + * + * @return + */ + TtSpuGetResult spuGet(TtSpuGet spuGet) throws WxErrorException ; + + /** + * 多门店SPU库存同步 + * https://open.douyin.com/platform/doc?doc=docs/openapi/life-service-open-ability/goods-repo/spu-repo-sync + * + * @return + */ + boolean spuStockUpdate(TtSpuStockSync spuStockSync) throws WxErrorException ; + +} diff --git a/mallinkService/src/main/java/com/iformall/douyin/web/api/TtWebService.java b/mallinkService/src/main/java/com/iformall/douyin/web/api/TtWebService.java index eef1b9fc1..7f33598fe 100644 --- a/mallinkService/src/main/java/com/iformall/douyin/web/api/TtWebService.java +++ b/mallinkService/src/main/java/com/iformall/douyin/web/api/TtWebService.java @@ -1,23 +1,21 @@ package com.iformall.douyin.web.api; -import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; import cn.binarywang.wx.miniapp.config.WxMaConfig; -import com.iformall.douyin.miniapp.api.TtMaQrcodeService; -import com.iformall.douyin.miniapp.api.TtMaUserService; +import com.iformall.douyin.web.config.TtWebConfig; import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor; import me.chanjar.weixin.common.util.http.RequestExecutor; import me.chanjar.weixin.common.util.http.RequestHttp; /** - * @author Binary Wang + * */ public interface TtWebService { /** * 获取access_token. */ - String GET_ACCESS_TOKEN_URL = "oauth/client_token"; + String POST_ACCESS_TOKEN_URL = "/oauth/client_token/"; /** @@ -29,27 +27,27 @@ public interface TtWebService { * * 程序员在非必要情况下尽量不要主动调用此方法 * - * 详情请见: https://microapp.bytedance.com/docs/zh-CN/mini-app/develop/server/interface-request-credential/get-access-token + * 详情请见: https://open.douyin.com/platform/doc/6848806493387573256?is_new_connect=0&is_new_user=0 * * * @param forceRefresh 强制刷新 */ String getAccessToken(boolean forceRefresh) throws WxErrorException; - /** - * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的GET请求. - */ - String get(String url, String queryParam) throws WxErrorException; - - /** - * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的POST请求. - */ - String post(String url, String postData) throws WxErrorException; - - /** - * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的POST请求. - */ - String post(String url, Object obj) throws WxErrorException; +// /** +// * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的GET请求. +// */ +// String get(String url, String queryParam) throws WxErrorException; +// +// /** +// * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的POST请求. +// */ +// String post(String url, String postData) throws WxErrorException; +// +// /** +// * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的POST请求. +// */ +// String post(String url, Object obj) throws WxErrorException; /** *
@@ -81,12 +79,12 @@ public interface TtWebService {
    *
    * @return WxMaConfig
    */
-  WxMaConfig getWxMaConfig();
+  TtWebConfig getTtWebConfig();
 
   /**
    * 注入 {@link WxMaConfig} 的实现.
    */
-  void setWxMaConfig(WxMaConfig wxConfigProvider);
+  void setTtWebConfig(TtWebConfig configProvider);
 
   /**
    * 初始化http请求对象.
@@ -103,13 +101,13 @@ public interface TtWebService {
    *
    * @return TtMaUserService
    */
-  TtMaUserService getUserService();
+  TtWebShopMatchService getShopMatchService();
 
   /**
    * 返回用户相关接口方法的实现类对象,以方便调用其各个接口.
    *
    * @return TtMaUserService
    */
-  TtMaQrcodeService getQrcodeService();
+  TtWebProductService getProductService();
 
 }
diff --git a/mallinkService/src/main/java/com/iformall/douyin/web/api/TtWebShopMatchService.java b/mallinkService/src/main/java/com/iformall/douyin/web/api/TtWebShopMatchService.java
new file mode 100644
index 000000000..8a804d370
--- /dev/null
+++ b/mallinkService/src/main/java/com/iformall/douyin/web/api/TtWebShopMatchService.java
@@ -0,0 +1,57 @@
+package com.iformall.douyin.web.api;
+
+import com.iformall.douyin.web.bean.TtSupplierMatchList;
+import me.chanjar.weixin.common.error.WxErrorException;
+
+/**
+ * 店铺poi匹配相关操作接口.
+ *
+ * @author
+ */
+public interface TtWebShopMatchService {
+
+  /**
+   *发起店铺匹配POI同步任务
+   */
+  String POST_SUPPLIER_MATCH = "/poi/v2/supplier/match/";
+  /**
+   *店铺匹配任务结果查询
+   */
+  String GET_SUPPLIER_MATCH_QUERY = "/poi/v2/supplier/query/task/";
+  /**
+   *店铺匹配状态查询
+   */
+  String GET_SUPPLIER_MATCH_STATUS = "/poi/v2/supplier/query/supplier/";
+
+  /**
+   * 发起店铺匹配POI同步任务
+   * https://open.douyin.com/platform/doc?doc=docs/openapi/life-service-open-ability/shop/poi-sync-task
+   * 1. POI 匹配有两种形式,人工文件上传匹配和接口发起匹配任务,这两种形式都是相同的功能,不会有差异。
+   * 4. 一个店铺Id,只能对应一个抖音POI Id,发起匹配任务时,请务必确保自己的店铺id的唯一性 (服务商店铺 ID 和 抖音POI ID 是一对一映射关系)
+   * @param
+   *  一个匹配任务的 match_data_list 尽量不要超过 1000条,但也不建议一个匹配任务只有一条匹配数据
+   *  匹配失败之后,可再次发起匹配
+   * 当店铺已经完成匹配或者正在匹配中,请不要重复提交匹配
+   * @return .上传成功后 返回 task_id 抖音平台任务ID
+   * @throws WxErrorException .
+   */
+  String supplierMatch(TtSupplierMatchList supplierMatchList) throws WxErrorException;
+
+  /**
+   * 店铺匹配任务结果查询
+   *
+   * @param supplierTaskIds
+   * supplier_task_ids		第三方上传任务id列表,多个任务id用 , 分割,单次查询最多10个任务。
+   */
+  TtSupplierMatchList supplierQueryTask(String supplierTaskIds) throws WxErrorException ;
+
+  /**
+   * 店铺匹配状态查询
+   *
+   * @param supplierExtId
+   * supplier_ext_id    第三方店铺id列表,多个商铺id用 , 分割,单次查询最多50个店铺。
+   * @return .
+   */
+  TtSupplierMatchList supplierQuerySupplier(String supplierExtId) throws WxErrorException ;
+
+}
diff --git a/mallinkService/src/main/java/com/iformall/douyin/web/api/impl/TtWebProductServiceImpl.java b/mallinkService/src/main/java/com/iformall/douyin/web/api/impl/TtWebProductServiceImpl.java
new file mode 100644
index 000000000..6159e6c3e
--- /dev/null
+++ b/mallinkService/src/main/java/com/iformall/douyin/web/api/impl/TtWebProductServiceImpl.java
@@ -0,0 +1,75 @@
+package com.iformall.douyin.web.api.impl;
+
+import com.alibaba.fastjson.JSONObject;
+import com.google.gson.JsonObject;
+import com.iformall.douyin.web.api.TtWebGetRequestExecutor;
+import com.iformall.douyin.web.api.TtWebPostRequestExecutor;
+import com.iformall.douyin.web.api.TtWebProductService;
+import com.iformall.douyin.web.api.TtWebService;
+import com.iformall.douyin.web.bean.*;
+import lombok.AllArgsConstructor;
+import me.chanjar.weixin.common.error.WxErrorException;
+
+/**
+ *
+ */
+@AllArgsConstructor
+public class TtWebProductServiceImpl implements TtWebProductService {
+
+  private TtWebService service;
+
+
+  @Override
+  public String supplierSync(TtSupplierSync supplierSync) throws WxErrorException {
+    final TtWebPostRequestExecutor executor = new TtWebPostRequestExecutor(this.service.getRequestHttp());
+    String apiUrl = this.service.getTtWebConfig().getApiBegin() + this.POST_SUPPLIER_SYNC;
+    String result = this.service.execute(executor, apiUrl, supplierSync.toJson());
+    JSONObject jsonObject = JSONObject.parseObject(result);
+    return jsonObject.getString("supplier_id");
+  }
+
+  @Override
+  public TtSupplierSyncQuery supplierQuery(String supplierExtId) throws WxErrorException {
+    final TtWebGetRequestExecutor executor = new TtWebGetRequestExecutor(this.service.getRequestHttp());
+    String apiUrl = this.service.getTtWebConfig().getApiBegin() + this.GET_SUPPLIER_QUERY;
+    JsonObject json = new JsonObject();
+    json.addProperty("supplier_ext_id",supplierExtId);
+    String result = this.service.execute(executor, apiUrl, json.toString());
+    return TtSupplierSyncQuery.fromJson(result);
+  }
+
+  @Override
+  public String spuSync(TtSpuSync spuSync) throws WxErrorException {
+    final TtWebPostRequestExecutor executor = new TtWebPostRequestExecutor(this.service.getRequestHttp());
+    String apiUrl = this.service.getTtWebConfig().getApiBegin() + this.POST_SPU_SYNC;
+    String result = this.service.execute(executor, apiUrl, spuSync.toJson());
+    JSONObject jsonObject = JSONObject.parseObject(result);
+    return jsonObject.getString("spu_id");
+  }
+
+  @Override
+  public TtSpuStatusSync spuStatusSync(TtSpuStatusSync spuStatusSync) throws WxErrorException {
+    final TtWebPostRequestExecutor executor = new TtWebPostRequestExecutor(this.service.getRequestHttp());
+    String apiUrl = this.service.getTtWebConfig().getApiBegin() + this.POST_SPU_STATUS_SYNC;
+    String result = this.service.execute(executor, apiUrl, spuStatusSync.toJson());
+    TtSpuStatusSync ttSpuStatusSync = TtSpuStatusSync.fromJson(result);
+    ttSpuStatusSync.setStatus(spuStatusSync.getStatus());
+    return ttSpuStatusSync;
+  }
+
+  @Override
+  public TtSpuGetResult spuGet(TtSpuGet spuGet) throws WxErrorException {
+    final TtWebGetRequestExecutor executor = new TtWebGetRequestExecutor(this.service.getRequestHttp());
+    String apiUrl = this.service.getTtWebConfig().getApiBegin() + this.GET_SPU_GET;
+    String result = this.service.execute(executor, apiUrl, spuGet.toString());
+    return TtSpuGetResult.fromJson(result);
+  }
+
+  @Override
+  public boolean spuStockUpdate(TtSpuStockSync spuStockSync) throws WxErrorException {
+    final TtWebPostRequestExecutor executor = new TtWebPostRequestExecutor(this.service.getRequestHttp());
+    String apiUrl = this.service.getTtWebConfig().getApiBegin() + this.POST_SPU_STOCK_UPDATE;
+    String result = this.service.execute(executor, apiUrl, spuStockSync.toJson());
+    return true;
+  }
+}
diff --git a/mallinkService/src/main/java/com/iformall/douyin/web/api/impl/TtWebServiceImpl.java b/mallinkService/src/main/java/com/iformall/douyin/web/api/impl/TtWebServiceImpl.java
new file mode 100644
index 000000000..dfb3de005
--- /dev/null
+++ b/mallinkService/src/main/java/com/iformall/douyin/web/api/impl/TtWebServiceImpl.java
@@ -0,0 +1,274 @@
+package com.iformall.douyin.web.api.impl;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.google.gson.Gson;
+import com.google.gson.JsonParser;
+import com.iformall.douyin.web.api.TtWebProductService;
+import com.iformall.douyin.web.api.TtWebService;
+import com.iformall.douyin.web.api.TtWebShopMatchService;
+import com.iformall.douyin.web.config.TtWebConfig;
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.common.WxType;
+import me.chanjar.weixin.common.bean.WxAccessToken;
+import me.chanjar.weixin.common.error.WxError;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.common.util.DataUtils;
+import me.chanjar.weixin.common.util.http.*;
+import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder;
+import me.chanjar.weixin.common.util.http.apache.DefaultApacheHttpClientBuilder;
+import me.chanjar.weixin.common.util.json.WxGsonBuilder;
+import org.apache.http.Consts;
+import org.apache.http.HttpHost;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.BasicResponseHandler;
+import org.apache.http.impl.client.CloseableHttpClient;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.locks.Lock;
+
+import static cn.binarywang.wx.miniapp.constant.WxMaConstants.ErrorCode.*;
+
+/**
+ * @author
+ */
+@Slf4j
+public class TtWebServiceImpl implements TtWebService, RequestHttp {
+  private static final JsonParser JSON_PARSER = new JsonParser();
+  private CloseableHttpClient httpClient;
+  private HttpHost httpProxy;
+  private TtWebConfig ttWebConfig;
+
+  private TtWebShopMatchService shopMatchService = new TtWebShopMatchServiceImpl(this);
+  private TtWebProductService productService = new TtWebProductServiceImpl(this);
+
+  private int retrySleepMillis = 1000;
+  private int maxRetryTimes = 5;
+
+  protected static final Gson GSON = new Gson();
+
+  @Override
+  public CloseableHttpClient getRequestHttpClient() {
+    return httpClient;
+  }
+
+  @Override
+  public HttpHost getRequestHttpProxy() {
+    return httpProxy;
+  }
+
+  @Override
+  public HttpType getRequestType() {
+    return HttpType.APACHE_HTTP;
+  }
+
+  @Override
+  public void initHttp() {
+    TtWebConfig configStorage = this.getTtWebConfig();
+    ApacheHttpClientBuilder apacheHttpClientBuilder = configStorage.getApacheHttpClientBuilder();
+    if (null == apacheHttpClientBuilder) {
+      apacheHttpClientBuilder = DefaultApacheHttpClientBuilder.get();
+    }
+
+    apacheHttpClientBuilder.httpProxyHost(configStorage.getHttpProxyHost())
+      .httpProxyPort(configStorage.getHttpProxyPort())
+      .httpProxyUsername(configStorage.getHttpProxyUsername())
+      .httpProxyPassword(configStorage.getHttpProxyPassword());
+
+    if (configStorage.getHttpProxyHost() != null && configStorage.getHttpProxyPort() > 0) {
+      this.httpProxy = new HttpHost(configStorage.getHttpProxyHost(), configStorage.getHttpProxyPort());
+    }
+
+    this.httpClient = apacheHttpClientBuilder.build();
+  }
+
+  @Override
+  public RequestHttp getRequestHttp() {
+    return this;
+  }
+
+  @Override
+  public String getAccessToken(boolean forceRefresh) throws WxErrorException {
+    if (!this.getTtWebConfig().isAccessTokenExpired() && !forceRefresh) {
+      return this.getTtWebConfig().getAccessToken();
+    }
+
+    Lock lock = this.getTtWebConfig().getAccessTokenLock();
+    lock.lock();
+    try {
+      Map params = new HashMap<>(8);
+      params.put("client_key", this.getTtWebConfig().getAppid());
+      params.put("client_secret", this.getTtWebConfig().getSecret());
+      params.put("grant_type", "client_credential");
+      try {
+        String apiUrl = this.getTtWebConfig().getApiBegin() + this.POST_ACCESS_TOKEN_URL;
+        HttpPost httpPost = new HttpPost(apiUrl);
+        if (this.getRequestHttpProxy() != null) {
+          RequestConfig config = RequestConfig.custom().setProxy(this.getRequestHttpProxy()).build();
+          httpPost.setConfig(config);
+        }
+        StringEntity entity = new StringEntity(WxGsonBuilder.create().toJson(params), Consts.UTF_8);
+        entity.setContentType("application/json; charset=utf-8");
+        httpPost.setEntity(entity);
+        try (CloseableHttpResponse response = getRequestHttpClient().execute(httpPost)) {
+          String resultContent = new BasicResponseHandler().handleResponse(response);
+          JSONObject jsonObject = JSON.parseObject(resultContent);
+          JSONObject data = jsonObject.getJSONObject("data");
+          WxError error = WxError.fromJson(data.toJSONString());
+          if (error.getErrorCode() != 0) {
+            throw new WxErrorException(error);
+          }
+          WxAccessToken accessToken = WxAccessToken.fromJson(data.toJSONString());
+          this.getTtWebConfig().updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn());
+          return this.getTtWebConfig().getAccessToken();
+        } finally {
+          httpPost.releaseConnection();
+        }
+      } catch (IOException e) {
+        throw new RuntimeException(e);
+      }
+    } finally {
+      lock.unlock();
+    }
+
+  }
+
+//  @Override
+//  public String get(String url, String queryParam) throws WxErrorException {
+//    return execute(SimpleGetRequestExecutor.create(this), url, queryParam);
+//  }
+//
+//  @Override
+//  public String post(String url, String postData) throws WxErrorException {
+//    return execute(SimplePostRequestExecutor.create(this), url, postData);
+//  }
+//
+//  @Override
+//  public String post(String url, Object obj) throws WxErrorException {
+//    return this.execute(SimplePostRequestExecutor.create(this), url, WxGsonBuilder.create().toJson(obj));
+//  }
+
+  /**
+   * 向微信端发送请求,在这里执行的策略是当发生access_token过期时才去刷新,然后重新执行请求,而不是全局定时请求
+   */
+  @Override
+  public  T execute(RequestExecutor executor, String uri, E data) throws WxErrorException {
+    int retryTimes = 0;
+    do {
+      try {
+        return this.executeInternal(executor, uri, data);
+      } catch (WxErrorException e) {
+        if (retryTimes + 1 > this.maxRetryTimes) {
+          log.warn("重试达到最大次数【{}】", maxRetryTimes);
+          //最后一次重试失败后,直接抛出异常,不再等待
+          throw new RuntimeException("微信服务端异常,超出重试次数");
+        }
+
+        WxError error = e.getError();
+        // -1 系统繁忙, 1000ms后重试
+        if (error.getErrorCode() == -1) {
+          int sleepMillis = this.retrySleepMillis * (1 << retryTimes);
+          try {
+            log.warn("微信系统繁忙,{} ms 后重试(第{}次)", sleepMillis, retryTimes + 1);
+            Thread.sleep(sleepMillis);
+          } catch (InterruptedException e1) {
+            Thread.currentThread().interrupt();
+          }
+        } else {
+          throw e;
+        }
+      }
+    } while (retryTimes++ < this.maxRetryTimes);
+
+    log.warn("重试达到最大次数【{}】", this.maxRetryTimes);
+    throw new RuntimeException("微信服务端异常,超出重试次数");
+  }
+
+  private  T executeInternal(RequestExecutor executor, String uri, E data) throws WxErrorException {
+    E dataForLog = DataUtils.handleDataWithSecret(data);
+
+    if (uri.contains("access_token=")) {
+      throw new IllegalArgumentException("uri参数中不允许有access_token: " + uri);
+    }
+    String accessToken = getAccessToken(false);
+    String uriWithAccessToken = uri + (uri.contains("?") ? "&" : "?") + "access_token=" + accessToken;
+//    try {
+//      Class aClass = data.getClass();
+//      if(!aClass.equals(String.class)){
+//        Field accessToken1 = aClass.getDeclaredField("access_token");
+//        accessToken1.setAccessible(true);
+//        accessToken1.set(data,accessToken);
+//        uriWithAccessToken = uri;
+//      }
+//    } catch (Exception e) {
+//      log.error("【请求参数】:{}添加accessToken失败" + data.getClass());
+//    }
+
+    try {
+      T result = executor.execute(uriWithAccessToken, data, WxType.MiniApp);
+      log.debug("\n【请求地址】: {}\n【请求参数】:{}\n【响应数据】:{}", uriWithAccessToken, data, result);
+      return result;
+    } catch (WxErrorException e) {
+      WxError error = e.getError();
+      /*
+       * 发生以下情况时尝试刷新access_token
+       */
+      if (error.getErrorCode() == ERR_40001
+        || error.getErrorCode() == ERR_42001
+        || error.getErrorCode() == ERR_40014) {
+        // 强制设置WxMaConfig的access token过期了,这样在下一次请求里就会刷新access token
+        this.getTtWebConfig().expireAccessToken();
+        if (this.getTtWebConfig().autoRefreshToken()) {
+          return this.execute(executor, uri, data);
+        }
+      }
+
+      if (error.getErrorCode() != 0) {
+        log.error("\n【请求地址】: {}\n【请求参数】:{}\n【错误信息】:{}", uriWithAccessToken, dataForLog, error);
+        throw new WxErrorException(error, e);
+      }
+      return null;
+    } catch (IOException e) {
+      log.error("\n【请求地址】: {}\n【请求参数】:{}\n【异常信息】:{}", uriWithAccessToken, dataForLog, e.getMessage());
+      throw new RuntimeException(e);
+    }
+  }
+
+  @Override
+  public TtWebConfig getTtWebConfig() {
+    return this.ttWebConfig;
+  }
+
+  @Override
+  public void setTtWebConfig(TtWebConfig configProvider) {
+    this.ttWebConfig = configProvider;
+    this.initHttp();
+  }
+
+  @Override
+  public void setRetrySleepMillis(int retrySleepMillis) {
+    this.retrySleepMillis = retrySleepMillis;
+  }
+
+  @Override
+  public void setMaxRetryTimes(int maxRetryTimes) {
+    this.maxRetryTimes = maxRetryTimes;
+  }
+
+  @Override
+  public TtWebShopMatchService getShopMatchService() {
+    return this.shopMatchService;
+  }
+
+  @Override
+  public TtWebProductService getProductService() {
+    return this.productService;
+  }
+
+}
diff --git a/mallinkService/src/main/java/com/iformall/douyin/web/api/impl/TtWebShopMatchServiceImpl.java b/mallinkService/src/main/java/com/iformall/douyin/web/api/impl/TtWebShopMatchServiceImpl.java
new file mode 100644
index 000000000..b4d5434ec
--- /dev/null
+++ b/mallinkService/src/main/java/com/iformall/douyin/web/api/impl/TtWebShopMatchServiceImpl.java
@@ -0,0 +1,52 @@
+package com.iformall.douyin.web.api.impl;
+
+import com.google.gson.JsonObject;
+import com.iformall.douyin.web.api.TtWebGetRequestExecutor;
+import com.iformall.douyin.web.api.TtWebPostRequestExecutor;
+import com.iformall.douyin.web.api.TtWebService;
+import com.iformall.douyin.web.api.TtWebShopMatchService;
+import com.iformall.douyin.web.bean.TtSupplierMatch;
+import com.iformall.douyin.web.bean.TtSupplierMatchList;
+import lombok.AllArgsConstructor;
+import me.chanjar.weixin.common.error.WxErrorException;
+
+/**
+ * @author
+ */
+@AllArgsConstructor
+public class TtWebShopMatchServiceImpl implements TtWebShopMatchService {
+
+  private TtWebService service;
+
+  @Override
+  public String supplierMatch(TtSupplierMatchList supplierMatchList) throws WxErrorException {
+    final TtWebPostRequestExecutor executor = new TtWebPostRequestExecutor(this.service.getRequestHttp());
+    String apiUrl = this.service.getTtWebConfig().getApiBegin() + this.POST_SUPPLIER_MATCH;
+    String result = this.service.execute(executor, apiUrl, supplierMatchList.toJson());
+    TtSupplierMatch match = TtSupplierMatch.fromJson(result);
+    if(match.getIsSuccess() != null && match.getIsSuccess().intValue() == 1){
+        return match.getTaskId();
+    }
+    return null;
+  }
+
+  @Override
+  public TtSupplierMatchList supplierQueryTask(String supplierTaskIds)  throws WxErrorException {
+    final TtWebGetRequestExecutor executor = new TtWebGetRequestExecutor(this.service.getRequestHttp());
+    String apiUrl = this.service.getTtWebConfig().getApiBegin() + this.GET_SUPPLIER_MATCH_QUERY;
+    JsonObject json = new JsonObject();
+    json.addProperty("supplier_task_ids",supplierTaskIds);
+    String result = this.service.execute(executor, apiUrl, json.toString());
+    return TtSupplierMatchList.fromJson(result);
+  }
+
+  @Override
+  public TtSupplierMatchList supplierQuerySupplier(String supplierExtId) throws WxErrorException  {
+    final TtWebGetRequestExecutor executor = new TtWebGetRequestExecutor(this.service.getRequestHttp());
+    String apiUrl = this.service.getTtWebConfig().getApiBegin() + this.GET_SUPPLIER_MATCH_STATUS;
+    JsonObject json = new JsonObject();
+    json.addProperty("supplier_ext_id",supplierExtId);
+    String result = this.service.execute(executor, apiUrl, json.toString());
+    return TtSupplierMatchList.fromJson(result);
+  }
+}
diff --git a/mallinkService/src/main/java/com/iformall/douyin/web/bean/TtSpuGet.java b/mallinkService/src/main/java/com/iformall/douyin/web/bean/TtSpuGet.java
new file mode 100644
index 000000000..84d1c6592
--- /dev/null
+++ b/mallinkService/src/main/java/com/iformall/douyin/web/bean/TtSpuGet.java
@@ -0,0 +1,30 @@
+package com.iformall.douyin.web.bean;
+
+import com.iformall.douyin.web.json.TtWebGsonBuilder;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ *
+ */
+@Data
+public class TtSpuGet implements Serializable {
+
+  private static final long serialVersionUID = 2724111691183398705L;
+
+  private String spuExtId;//第三方SPU ID*****
+  private boolean needSpuDraft;//是否需要商品草稿数据(带有商品的审核状态,只展示最近30天的数据,并且最多最近提交的20次内****
+  private Integer spuDraftcount;//需要商品草稿数据的数目(0-20),超过这个范围默认返回20个
+  private List supplierIdsForFilterReason;//供应商id列表,需要商品在某供应商下的过滤状态
+
+  public static TtSpuGet fromJson(String json) {
+    return TtWebGsonBuilder.create().fromJson(json, TtSpuGet.class);
+  }
+
+  public String toJson() {
+    return TtWebGsonBuilder.create().toJson(this);
+  }
+
+}
diff --git a/mallinkService/src/main/java/com/iformall/douyin/web/bean/TtSpuGetResult.java b/mallinkService/src/main/java/com/iformall/douyin/web/bean/TtSpuGetResult.java
new file mode 100644
index 000000000..74c0d2c24
--- /dev/null
+++ b/mallinkService/src/main/java/com/iformall/douyin/web/bean/TtSpuGetResult.java
@@ -0,0 +1,29 @@
+package com.iformall.douyin.web.bean;
+
+import com.iformall.douyin.web.json.TtWebGsonBuilder;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ *
+ */
+@Data
+public class TtSpuGetResult implements Serializable {
+
+  private static final long serialVersionUID = -4041080958809605624L;
+
+  private TtSpuSync spuDetail;
+
+  private  List spuDraft;
+
+  public static TtSpuGetResult fromJson(String json) {
+    return TtWebGsonBuilder.create().fromJson(json, TtSpuGetResult.class);
+  }
+
+  public String toJson() {
+    return TtWebGsonBuilder.create().toJson(this);
+  }
+
+}
diff --git a/mallinkService/src/main/java/com/iformall/douyin/web/bean/TtSpuStatusSync.java b/mallinkService/src/main/java/com/iformall/douyin/web/bean/TtSpuStatusSync.java
new file mode 100644
index 000000000..87f0585c3
--- /dev/null
+++ b/mallinkService/src/main/java/com/iformall/douyin/web/bean/TtSpuStatusSync.java
@@ -0,0 +1,28 @@
+package com.iformall.douyin.web.bean;
+
+import com.iformall.douyin.web.json.TtWebGsonBuilder;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ *
+ */
+@Data
+public class TtSpuStatusSync implements Serializable {
+
+  private static final long serialVersionUID = 8681432858348826414L;
+
+  private List spuExtIdList;//接入方商品ID列表
+  private Integer status;	//SPU状态, 1 - 在线; 2 - 下线
+
+  public static TtSpuStatusSync fromJson(String json) {
+    return TtWebGsonBuilder.create().fromJson(json, TtSpuStatusSync.class);
+  }
+
+  public String toJson() {
+    return TtWebGsonBuilder.create().toJson(this);
+  }
+
+}
diff --git a/mallinkService/src/main/java/com/iformall/douyin/web/bean/TtSpuStockSync.java b/mallinkService/src/main/java/com/iformall/douyin/web/bean/TtSpuStockSync.java
new file mode 100644
index 000000000..e65621428
--- /dev/null
+++ b/mallinkService/src/main/java/com/iformall/douyin/web/bean/TtSpuStockSync.java
@@ -0,0 +1,28 @@
+package com.iformall.douyin.web.bean;
+
+import com.iformall.douyin.web.json.TtWebGsonBuilder;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ *
+ */
+@Data
+public class TtSpuStockSync implements Serializable {
+
+  private static final long serialVersionUID = -5212825199314022817L;
+
+  private String spuExtId;//接入方商品ID
+  private Integer stock;	//库存
+
+  public static TtSpuStockSync fromJson(String json) {
+    return TtWebGsonBuilder.create().fromJson(json, TtSpuStockSync.class);
+  }
+
+  public String toJson() {
+    return TtWebGsonBuilder.create().toJson(this);
+  }
+
+}
diff --git a/mallinkService/src/main/java/com/iformall/douyin/web/bean/TtSpuSync.java b/mallinkService/src/main/java/com/iformall/douyin/web/bean/TtSpuSync.java
new file mode 100644
index 000000000..8c944388a
--- /dev/null
+++ b/mallinkService/src/main/java/com/iformall/douyin/web/bean/TtSpuSync.java
@@ -0,0 +1,70 @@
+package com.iformall.douyin.web.bean;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+import com.iformall.douyin.web.json.TtWebGsonBuilder;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ *
+ */
+@Data
+public class TtSpuSync implements Serializable {
+
+  private static final long serialVersionUID = -132141170587251217L;
+  /**
+   * 入口信息
+   * entry_miniApp    onject     小程序入口参数
+   *                  is_test     int     已废弃字段,请勿传入
+   *                  params      String   	服务参数json
+   *                  path        String    服务路径
+   *                  app_id      String    小程序的appid
+   * entry_type       int         入口类型(1:H5,2:抖音小程序,3:抖音链接)
+   * entry_url        String      入口链接
+   *
+   */
+  private JsonObject entryInfo;//入口信息
+  private List frontCategoryTag;//	前台品类标签
+
+  private String recommendWord;//推荐语,5~20个字
+  private Integer sortWeight;//排序权重,按降序排列
+  private List supplierExtIdList;//接入方店铺ID列表*******************
+  private Integer price;//价格,单位分***********************
+  private String spuExtId;//接入方商品ID********************
+  private List imageList;//SPU图片,预售券必传*******************
+  private Integer mpSettletype;//小程序结算类型 1-包销 2-代销
+  private Integer originPrice;//原价格,单位分 *************
+  private Integer takeRate;//商品的抽佣率,万分数
+
+  /**
+   * SPU属性字段
+   * 9101 团购劵
+   */
+  private JsonObject attribute;//SPU属性字段**********************
+
+  /**
+   * 商品亮点标签    object[]
+   * content   String    介绍,字符串长度<=5
+   * priority  int       优先级,数字越小优先级越高
+   *
+   */
+  private JsonArray highlights;//商品亮点标签
+  private String name;//商品名 **********
+  private Boolean orderDependsOnDate;//下单是否依赖日期
+  private Integer spuType;//spu类型号,1-日历房,30-酒店民宿预售券,90-门票,91-团购券**********
+  private Integer status;//SPU状态, 1 - 在线; 2 - 下线**********
+  private Integer stock;//库存*********
+
+
+  public static TtSpuSync fromJson(String json) {
+    return TtWebGsonBuilder.create().fromJson(json, TtSpuSync.class);
+  }
+
+  public String toJson() {
+    return TtWebGsonBuilder.create().toJson(this);
+  }
+
+}
diff --git a/mallinkService/src/main/java/com/iformall/douyin/web/bean/TtSupplierMatch.java b/mallinkService/src/main/java/com/iformall/douyin/web/bean/TtSupplierMatch.java
new file mode 100644
index 000000000..d94863a86
--- /dev/null
+++ b/mallinkService/src/main/java/com/iformall/douyin/web/bean/TtSupplierMatch.java
@@ -0,0 +1,42 @@
+package com.iformall.douyin.web.bean;
+
+import com.iformall.douyin.web.json.TtWebGsonBuilder;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ *
+ */
+@Data
+public class TtSupplierMatch implements Serializable {
+
+  private static final long serialVersionUID = 1551210699660483749L;
+
+  private String city;//POI所在城市
+  private Float latitude;//纬度
+  private Float longitude;//经度
+  private String amapId;//高德POI ID
+  private String extra;//其他信息
+  private String poiId;//高德POI ID/抖音POIID
+  private String poiName;//POI名称
+  private String province;//POI所在省份
+  private String supplierExtId;//第三方商户ID
+  private String address;//	POI地址
+
+  //通过 task_id 查询时状态
+  private Integer matchStatus;//匹配状态,0-等待匹配,1-正在匹配,2-匹配成功,3-匹配失败
+  private String mismatchStatusDesc;//匹配状态描述
+
+  //通过 supplier_ext_id 查询时状态
+  private Integer status;//匹配状态,0-没有匹配,1-匹配中,2-匹配完成,3-匹配失败
+  private String taskId;//查询时匹配任务Id
+
+  //匹配时状态
+  private Integer isSuccess;//上传状态(1:成功,2:失败)
+
+  public static TtSupplierMatch fromJson(String json) {
+    return TtWebGsonBuilder.create().fromJson(json, TtSupplierMatch.class);
+  }
+
+}
diff --git a/mallinkService/src/main/java/com/iformall/douyin/web/bean/TtSupplierMatchList.java b/mallinkService/src/main/java/com/iformall/douyin/web/bean/TtSupplierMatchList.java
new file mode 100644
index 000000000..d7e3776e3
--- /dev/null
+++ b/mallinkService/src/main/java/com/iformall/douyin/web/bean/TtSupplierMatchList.java
@@ -0,0 +1,27 @@
+package com.iformall.douyin.web.bean;
+
+import com.iformall.douyin.web.json.TtWebGsonBuilder;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ *
+ */
+@Data
+public class TtSupplierMatchList implements Serializable {
+
+  private static final long serialVersionUID = 3957713502355504672L;
+
+  private List matchDataList = new ArrayList<>();
+
+  public static TtSupplierMatchList fromJson(String json) {
+    return TtWebGsonBuilder.create().fromJson(json, TtSupplierMatchList.class);
+  }
+
+  public String toJson() {
+    return TtWebGsonBuilder.create().toJson(this);
+  }
+}
diff --git a/mallinkService/src/main/java/com/iformall/douyin/web/bean/TtSupplierSync.java b/mallinkService/src/main/java/com/iformall/douyin/web/bean/TtSupplierSync.java
new file mode 100644
index 000000000..b92c5b8aa
--- /dev/null
+++ b/mallinkService/src/main/java/com/iformall/douyin/web/bean/TtSupplierSync.java
@@ -0,0 +1,100 @@
+package com.iformall.douyin.web.bean;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+import com.iformall.douyin.web.json.TtWebGsonBuilder;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ *
+ */
+@Data
+public class TtSupplierSync implements Serializable {
+
+  private static final long serialVersionUID = -3267812748743441836L;
+
+  private String contactPhone;//联系手机号
+  private String contactTel;//联系座机号
+  private List images;//店铺图片
+  private String merchantUid;//商户号;商家担保交易中的收款账户ID
+  /**
+   * 服务商资质信息(json )
+   * business_license_ext_id    string      服务商营业执照
+   * industry_license_ext_id    string[]    服务商行业许可证
+   */
+  private JsonObject serviceProvider;//服务商资质信息
+
+  private String supplierExtId;//接入方店铺id *************
+  private List tags;//标签 [可停车 离地铁近]
+  private String latitude;//纬度
+  private Integer status;//在线状态 1 - 在线; 2 - 下线 **********
+  private String typeCode;//POI品类编码
+  private String typeName;//POI品类描述 eg. 美食;中式餐饮;小龙虾
+  private String address;//店铺地址
+  private Integer avgCost;//人均消费(单位分)
+  /**
+   *商家资质信息
+   * power_of_attorney  object 服务商和商家合作协议/授意书
+   *                  ext_id   string  合作协议/授意书外部id
+   *                  url      string  合作协议/授意书链接
+   * business_license   object 商家营业执照
+   *                  company  string  服务上营业执照公司名称
+   *                  ext_id   string  商家营业执照外部id
+   *                  url      string  合作协议/授意书链接
+   * industry_license   object[]行业许可证
+   *                  ext_id   string  商家营业执行业许可证外部id
+   *                  url      string  商家行业许可证链接
+   * other_info         object[]其他补充材料
+   *                  ext_id   string  其他补充材料外部id
+   *                  url      string  其他补充外部链接
+   */
+  private JsonObject customerInfo;//商家资质信息
+
+  private String description;//店铺介绍(<=500字)
+  private String name;//店铺名称**********************************
+
+  /**
+   * 推荐
+   * image_url     string   推荐内容链接(图片,或者视频链接)
+   * title         string   推荐描述
+   */
+  private JsonArray recommends;//推荐
+
+  /**
+   * 店铺提供的服务列表
+   * enable       int           上线状态(1:上线,2:下线)
+   * entry        object    	服务入口拼接参数
+   *              entry_mini_app       object       抖音小程序入口参数
+   *                                    is_test      int       主要用于联调,1-使用测试版的小程序,0或者不填-使用正式版小程序
+   *                                    params       string    服务参数json
+   *                                    path         string    服务路径
+   *                                    app_id       string    小程序的appid
+   *              entry_type           int           入口类型(1:H5,2:抖音小程序)
+   *              service_type         int           服务类型(201-预约点餐服务, 202-预约订位服务, 203-排队取号服务, 9001-门票预订服务, 9101-团购套餐服务, 9102-领优惠劵服务, 9201-在线预约服务, 9301-外卖服务, 40-住宿服务, 200-预约券服务)
+   */
+  private JsonArray services;//店铺提供的服务列表
+
+  /**
+   *店铺属性字段,编号规则:垂直行业 1xxx-酒店民宿 2xxx-餐饮 3xxx-景区 通用属性-9yxxx
+   * 1101          JsonArray         酒店服务
+   * *************
+   */
+  private JsonObject attributes;//店铺属性字段,编号规则:垂直行业 1xxx-酒店民宿 2xxx-餐饮 3xxx-景区 通用属性-9yxxx
+
+  private String longitude;//经度
+  private List openTime;//营业时间, 从周一到周日,list长度为7,不营业则为空字符串
+  private String poiId;//抖音poi id, 三方如果使用高德poi id可以通过/poi/query/接口转换,其它三方poi id走poi匹配功能进行抖音poi id获取*************
+  private Integer type;//店铺类型 1 - 酒店民宿 2 - 餐饮 3 - 景区 4 - 电商 5 - 教育 6 - 丽人 7 - 爱车 8 - 亲子 9 - 宠物 10 - 家装 11 - 娱乐场所 12 - 图文快印*****************
+
+  public static TtSupplierSync fromJson(String json) {
+    return TtWebGsonBuilder.create().fromJson(json, TtSupplierSync.class);
+  }
+
+  public String toJson() {
+    return TtWebGsonBuilder.create().toJson(this);
+  }
+
+}
diff --git a/mallinkService/src/main/java/com/iformall/douyin/web/bean/TtSupplierSyncQuery.java b/mallinkService/src/main/java/com/iformall/douyin/web/bean/TtSupplierSyncQuery.java
new file mode 100644
index 000000000..d492b134c
--- /dev/null
+++ b/mallinkService/src/main/java/com/iformall/douyin/web/bean/TtSupplierSyncQuery.java
@@ -0,0 +1,30 @@
+package com.iformall.douyin.web.bean;
+
+import com.google.gson.JsonObject;
+import com.iformall.douyin.web.json.TtWebGsonBuilder;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ *
+ */
+@Data
+public class TtSupplierSyncQuery implements Serializable {
+
+  private static final long serialVersionUID = -2559137138249012354L;
+
+  private TtSupplierSync dataDetail;
+
+  private TtSupplierSyncStatus syncStatus; //数据同步结果
+
+  public static TtSupplierSyncQuery fromJson(String json) {
+    return TtWebGsonBuilder.create().fromJson(json, TtSupplierSyncQuery.class);
+  }
+
+  public String toJson() {
+    return TtWebGsonBuilder.create().toJson(this);
+  }
+
+
+}
diff --git a/mallinkService/src/main/java/com/iformall/douyin/web/bean/TtSupplierSyncStatus.java b/mallinkService/src/main/java/com/iformall/douyin/web/bean/TtSupplierSyncStatus.java
new file mode 100644
index 000000000..6fe3035dc
--- /dev/null
+++ b/mallinkService/src/main/java/com/iformall/douyin/web/bean/TtSupplierSyncStatus.java
@@ -0,0 +1,18 @@
+package com.iformall.douyin.web.bean;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ *
+ */
+@Data
+public class TtSupplierSyncStatus implements Serializable {
+
+
+  private Integer lastSyncStatus;//最近一次同步状态, 0 - 未处理; 1 - 通过; 2 - 未通过
+
+  private String failReason; //同步失败原因,抖音风控政策问题,该字段无法提供太多信息,目前审核不通过联系抖音运营做进一步处理
+
+}
diff --git a/mallinkService/src/main/java/com/iformall/douyin/web/config/TtWebConfig.java b/mallinkService/src/main/java/com/iformall/douyin/web/config/TtWebConfig.java
new file mode 100644
index 000000000..1c2a6c5ae
--- /dev/null
+++ b/mallinkService/src/main/java/com/iformall/douyin/web/config/TtWebConfig.java
@@ -0,0 +1,76 @@
+package com.iformall.douyin.web.config;
+
+import com.iformall.douyin.web.enums.TtWebApiBeginEnum;
+import me.chanjar.weixin.common.bean.WxAccessToken;
+import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder;
+
+import java.util.concurrent.locks.Lock;
+
+/**
+ * 小程序配置
+ *
+ * @author Binary Wang
+ */
+public interface TtWebConfig {
+
+  String getAccessToken();
+
+  Lock getAccessTokenLock();
+
+  boolean isAccessTokenExpired();
+
+  /**
+   * 强制将access token过期掉
+   */
+  void expireAccessToken();
+
+  /**
+   * 应该是线程安全的
+   *
+   * @param accessToken 要更新的WxAccessToken对象
+   */
+  void updateAccessToken(WxAccessToken accessToken);
+
+  /**
+   * 应该是线程安全的
+   *
+   * @param accessToken      新的accessToken值
+   * @param expiresInSeconds 过期时间,以秒为单位
+   */
+  void updateAccessToken(String accessToken, int expiresInSeconds);
+
+  String getAppid();
+
+  String getSecret();
+
+  String getToken();
+
+  String getAesKey();
+
+  TtWebApiBeginEnum getApiBegin();
+
+  String getMsgDataFormat();
+
+  long getExpiresTime();
+
+  String getHttpProxyHost();
+
+  int getHttpProxyPort();
+
+  String getHttpProxyUsername();
+
+  String getHttpProxyPassword();
+
+  /**
+   * http client builder
+   *
+   * @return ApacheHttpClientBuilder
+   */
+  ApacheHttpClientBuilder getApacheHttpClientBuilder();
+
+  /**
+   * 是否自动刷新token
+   */
+  boolean autoRefreshToken();
+
+}
diff --git a/mallinkService/src/main/java/com/iformall/douyin/web/config/TtWebDefaultConfigImpl.java b/mallinkService/src/main/java/com/iformall/douyin/web/config/TtWebDefaultConfigImpl.java
new file mode 100644
index 000000000..459f90f7e
--- /dev/null
+++ b/mallinkService/src/main/java/com/iformall/douyin/web/config/TtWebDefaultConfigImpl.java
@@ -0,0 +1,214 @@
+package com.iformall.douyin.web.config;
+
+import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder;
+import com.iformall.douyin.web.enums.TtWebApiBeginEnum;
+import me.chanjar.weixin.common.bean.WxAccessToken;
+import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder;
+
+import java.io.File;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * 基于内存的微信配置provider,在实际生产环境中应该将这些配置持久化
+ *
+ * @author Binary Wang
+ */
+public class TtWebDefaultConfigImpl implements TtWebConfig {
+
+  private volatile TtWebApiBeginEnum apiBegin;
+
+  private volatile String msgDataFormat;
+  protected volatile String appid;
+  private volatile String secret;
+  protected volatile String token;
+  private volatile String accessToken;
+  private volatile String aesKey;
+  private volatile long expiresTime;
+
+
+  private volatile String httpProxyHost;
+  private volatile int httpProxyPort;
+  private volatile String httpProxyUsername;
+  private volatile String httpProxyPassword;
+
+  protected Lock accessTokenLock = new ReentrantLock();
+
+  /**
+   * 临时文件目录.
+   */
+  protected volatile File tmpDirFile;
+
+  private volatile ApacheHttpClientBuilder apacheHttpClientBuilder;
+
+  /**
+   * 会过期的数据提前过期时间,默认预留200秒的时间
+   */
+  protected long expiresAheadInMillis(int expiresInSeconds) {
+    return System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L;
+  }
+
+  /**
+   * 判断 expiresTime 是否已经过期
+   */
+  protected boolean isExpired(long expiresTime) {
+    return System.currentTimeMillis() > expiresTime;
+  }
+
+  @Override
+  public String getAccessToken() {
+    return this.accessToken;
+  }
+
+  public void setAccessToken(String accessToken) {
+    this.accessToken = accessToken;
+  }
+
+  @Override
+  public Lock getAccessTokenLock() {
+    return this.accessTokenLock;
+  }
+
+  public void setAccessTokenLock(Lock accessTokenLock) {
+    this.accessTokenLock = accessTokenLock;
+  }
+
+  @Override
+  public boolean isAccessTokenExpired() {
+    return isExpired(this.expiresTime);
+  }
+
+  @Override
+  public synchronized void updateAccessToken(WxAccessToken accessToken) {
+    updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn());
+  }
+
+  @Override
+  public synchronized void updateAccessToken(String accessToken, int expiresInSeconds) {
+    setAccessToken(accessToken);
+    setExpiresTime(expiresAheadInMillis(expiresInSeconds));
+  }
+
+  @Override
+  public void expireAccessToken() {
+    this.expiresTime = 0;
+  }
+
+  @Override
+  public String getSecret() {
+    return this.secret;
+  }
+
+  public void setSecret(String secret) {
+    this.secret = secret;
+  }
+
+  @Override
+  public String getToken() {
+    return this.token;
+  }
+
+  public void setToken(String token) {
+    this.token = token;
+  }
+
+  @Override
+  public long getExpiresTime() {
+    return this.expiresTime;
+  }
+
+  public void setExpiresTime(long expiresTime) {
+    this.expiresTime = expiresTime;
+  }
+
+  @Override
+  public String getAesKey() {
+    return this.aesKey;
+  }
+
+  public void setAesKey(String aesKey) {
+    this.aesKey = aesKey;
+  }
+
+  @Override
+  public TtWebApiBeginEnum getApiBegin() {
+    return this.apiBegin;
+  }
+
+  public void setApiBegin(TtWebApiBeginEnum apiBegin) {
+    this.apiBegin = apiBegin;
+  }
+
+  @Override
+  public String getMsgDataFormat() {
+    return this.msgDataFormat;
+  }
+
+  public void setMsgDataFormat(String msgDataFormat) {
+    this.msgDataFormat = msgDataFormat;
+  }
+
+  @Override
+  public String getHttpProxyHost() {
+    return this.httpProxyHost;
+  }
+
+  public void setHttpProxyHost(String httpProxyHost) {
+    this.httpProxyHost = httpProxyHost;
+  }
+
+  @Override
+  public int getHttpProxyPort() {
+    return this.httpProxyPort;
+  }
+
+  public void setHttpProxyPort(int httpProxyPort) {
+    this.httpProxyPort = httpProxyPort;
+  }
+
+  @Override
+  public String getHttpProxyUsername() {
+    return this.httpProxyUsername;
+  }
+
+  public void setHttpProxyUsername(String httpProxyUsername) {
+    this.httpProxyUsername = httpProxyUsername;
+  }
+
+  @Override
+  public String getHttpProxyPassword() {
+    return this.httpProxyPassword;
+  }
+
+  public void setHttpProxyPassword(String httpProxyPassword) {
+    this.httpProxyPassword = httpProxyPassword;
+  }
+
+  @Override
+  public String toString() {
+    return WxMaGsonBuilder.create().toJson(this);
+  }
+
+  @Override
+  public ApacheHttpClientBuilder getApacheHttpClientBuilder() {
+    return this.apacheHttpClientBuilder;
+  }
+
+  public void setApacheHttpClientBuilder(ApacheHttpClientBuilder apacheHttpClientBuilder) {
+    this.apacheHttpClientBuilder = apacheHttpClientBuilder;
+  }
+
+  @Override
+  public boolean autoRefreshToken() {
+    return true;
+  }
+
+  @Override
+  public String getAppid() {
+    return appid;
+  }
+
+  public void setAppid(String appid) {
+    this.appid = appid;
+  }
+}
diff --git a/mallinkService/src/main/java/com/iformall/douyin/web/json/TtSpuGetAdapter.java b/mallinkService/src/main/java/com/iformall/douyin/web/json/TtSpuGetAdapter.java
new file mode 100644
index 000000000..37cdfd247
--- /dev/null
+++ b/mallinkService/src/main/java/com/iformall/douyin/web/json/TtSpuGetAdapter.java
@@ -0,0 +1,55 @@
+/*
+ * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved.
+ *
+ * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended
+ * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction
+ * arose from modification of the original source, or other redistribution of this source
+ * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD.
+ */
+package com.iformall.douyin.web.json;
+
+import com.alibaba.fastjson.JSONArray;
+import com.google.gson.*;
+import com.iformall.douyin.web.bean.TtSpuGet;
+import me.chanjar.weixin.common.util.json.GsonHelper;
+
+import java.lang.reflect.Type;
+import java.util.Arrays;
+import java.util.Collections;
+
+
+/**
+ * @author Daniel Qian
+ */
+public class TtSpuGetAdapter implements JsonSerializer, JsonDeserializer {
+
+  @Override
+  public JsonElement serialize(TtSpuGet spuGet, Type typeOfSrc, JsonSerializationContext context) {
+    return convertToJson(spuGet);
+  }
+
+  protected JsonObject convertToJson(TtSpuGet spuGet) {
+    JsonObject json = new JsonObject();
+    json.addProperty("spu_ext_id", spuGet.getSpuExtId());
+    json.addProperty("need_spu_draft", spuGet.isNeedSpuDraft());
+    json.addProperty("spu_draft_count", spuGet.getSpuDraftcount());
+    if(spuGet.getSupplierIdsForFilterReason() != null && !spuGet.getSupplierIdsForFilterReason().isEmpty()){
+      json.addProperty("supplier_ids_for_filter_reason", new JSONArray(Collections.singletonList(spuGet.getSupplierIdsForFilterReason())).toJSONString());
+    }
+    return json;
+  }
+
+  @Override
+  public TtSpuGet deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
+    return convertFromJson(json.getAsJsonObject());
+  }
+
+  protected TtSpuGet convertFromJson(JsonObject json) {
+    TtSpuGet spuGet = new TtSpuGet();
+    spuGet.setSpuExtId(GsonHelper.getString(json, "spu_ext_id"));
+    spuGet.setNeedSpuDraft(GsonHelper.getBoolean(json,"need_spu_draft"));
+    spuGet.setSpuDraftcount(GsonHelper.getInteger(json,"spu_draft_count"));
+    spuGet.setSupplierIdsForFilterReason(Arrays.asList(GsonHelper.getStringArray(json, "supplier_ids_for_filter_reason")));
+    return spuGet;
+  }
+}
diff --git a/mallinkService/src/main/java/com/iformall/douyin/web/json/TtSpuGetResultAdapter.java b/mallinkService/src/main/java/com/iformall/douyin/web/json/TtSpuGetResultAdapter.java
new file mode 100644
index 000000000..1a1afd7ec
--- /dev/null
+++ b/mallinkService/src/main/java/com/iformall/douyin/web/json/TtSpuGetResultAdapter.java
@@ -0,0 +1,59 @@
+/*
+ * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved.
+ *
+ * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended
+ * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction
+ * arose from modification of the original source, or other redistribution of this source
+ * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD.
+ */
+package com.iformall.douyin.web.json;
+
+import com.google.gson.*;
+import com.iformall.douyin.web.bean.*;
+
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * @author Daniel Qian
+ */
+public class TtSpuGetResultAdapter implements JsonSerializer, JsonDeserializer {
+
+  @Override
+  public JsonElement serialize(TtSpuGetResult spuGetResult, Type typeOfSrc, JsonSerializationContext context) {
+    JsonObject json = new JsonObject();
+    JsonElement jsonElement = TtWebGsonBuilder.create().toJsonTree(spuGetResult.getSpuDetail());
+    json.add("spu_detail", jsonElement);
+    JsonArray jsonArray = new JsonArray();
+    if(spuGetResult.getSpuDraft() != null && !spuGetResult.getSpuDraft().isEmpty()){
+      for (TtSpuSync spuSync:spuGetResult.getSpuDraft()) {
+        JsonElement jsonElement1 = TtWebGsonBuilder.create().toJsonTree(spuSync);
+        jsonArray.add(jsonElement1);
+      }
+    }
+    json.add("spu_draft", jsonArray);
+    return json;
+  }
+
+  @Override
+  public TtSpuGetResult deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
+    TtSpuGetResult spuGetResult = new TtSpuGetResult();
+    JsonObject syncObject = json.getAsJsonObject().get("spu_detail").getAsJsonObject();
+    TtSpuSync spuSync = TtWebGsonBuilder.create().fromJson(syncObject, TtSpuSync.class);
+    spuGetResult.setSpuDetail(spuSync);
+    List spuDraft = new ArrayList<>();
+    JsonArray jsonArray = json.getAsJsonObject().get("spu_draft").getAsJsonArray();
+    if(jsonArray != null && jsonArray.size() > 0){
+      for (int i = 0;i < jsonArray.size(); i++){
+        JsonObject asJsonObject = jsonArray.get(i).getAsJsonObject();
+        TtSpuSync spuSync1 = TtWebGsonBuilder.create().fromJson(asJsonObject, TtSpuSync.class);
+        spuDraft.add(spuSync1);
+      }
+    }
+    spuGetResult.setSpuDraft(spuDraft);
+    return spuGetResult;
+  }
+
+}
diff --git a/mallinkService/src/main/java/com/iformall/douyin/web/json/TtSpuStatusSyncAdapter.java b/mallinkService/src/main/java/com/iformall/douyin/web/json/TtSpuStatusSyncAdapter.java
new file mode 100644
index 000000000..4718ead47
--- /dev/null
+++ b/mallinkService/src/main/java/com/iformall/douyin/web/json/TtSpuStatusSyncAdapter.java
@@ -0,0 +1,51 @@
+/*
+ * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved.
+ *
+ * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended
+ * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction
+ * arose from modification of the original source, or other redistribution of this source
+ * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD.
+ */
+package com.iformall.douyin.web.json;
+
+import com.alibaba.fastjson.JSONArray;
+import com.google.gson.*;
+import com.iformall.douyin.web.bean.TtSpuStatusSync;
+import me.chanjar.weixin.common.util.json.GsonHelper;
+
+import java.lang.reflect.Type;
+import java.util.Arrays;
+import java.util.Collections;
+
+
+/**
+ * @author Daniel Qian
+ */
+public class TtSpuStatusSyncAdapter implements JsonSerializer, JsonDeserializer {
+
+  @Override
+  public JsonElement serialize(TtSpuStatusSync spuStatusSync, Type typeOfSrc, JsonSerializationContext context) {
+    return convertToJson(spuStatusSync);
+  }
+
+  protected JsonObject convertToJson(TtSpuStatusSync spuSync) {
+    JsonObject json = new JsonObject();
+    if(spuSync.getSpuExtIdList() != null && !spuSync.getSpuExtIdList().isEmpty()){
+      json.addProperty("spu_ext_id_list", new JSONArray(Collections.singletonList(spuSync.getSpuExtIdList())).toJSONString());
+    }
+    json.addProperty("status", spuSync.getStatus());
+    return json;
+  }
+
+  @Override
+  public TtSpuStatusSync deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
+    return convertFromJson(json.getAsJsonObject());
+  }
+
+  protected TtSpuStatusSync convertFromJson(JsonObject json) {
+    TtSpuStatusSync spuSync = new TtSpuStatusSync();
+    spuSync.setSpuExtIdList(Arrays.asList(GsonHelper.getStringArray(json, "spu_ext_id_list")));
+    spuSync.setStatus(GsonHelper.getInteger(json, "status"));
+    return spuSync;
+  }
+}
diff --git a/mallinkService/src/main/java/com/iformall/douyin/web/json/TtSpuStockSyncAdapter.java b/mallinkService/src/main/java/com/iformall/douyin/web/json/TtSpuStockSyncAdapter.java
new file mode 100644
index 000000000..26414df81
--- /dev/null
+++ b/mallinkService/src/main/java/com/iformall/douyin/web/json/TtSpuStockSyncAdapter.java
@@ -0,0 +1,46 @@
+/*
+ * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved.
+ *
+ * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended
+ * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction
+ * arose from modification of the original source, or other redistribution of this source
+ * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD.
+ */
+package com.iformall.douyin.web.json;
+
+import com.google.gson.*;
+import com.iformall.douyin.web.bean.TtSpuStockSync;
+import me.chanjar.weixin.common.util.json.GsonHelper;
+
+import java.lang.reflect.Type;
+
+
+/**
+ * @author Daniel Qian
+ */
+public class TtSpuStockSyncAdapter implements JsonSerializer, JsonDeserializer {
+
+  @Override
+  public JsonElement serialize(TtSpuStockSync spuStockSync, Type typeOfSrc, JsonSerializationContext context) {
+    return convertToJson(spuStockSync);
+  }
+
+  protected JsonObject convertToJson(TtSpuStockSync spuStockSync) {
+    JsonObject json = new JsonObject();
+    json.addProperty("spu_ext_id", spuStockSync.getSpuExtId());
+    json.addProperty("stock", spuStockSync.getStock());
+    return json;
+  }
+
+  @Override
+  public TtSpuStockSync deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
+    return convertFromJson(json.getAsJsonObject());
+  }
+
+  protected TtSpuStockSync convertFromJson(JsonObject json) {
+    TtSpuStockSync spuStockSync = new TtSpuStockSync();
+    spuStockSync.setSpuExtId(GsonHelper.getString(json, "spu_ext_id"));
+    spuStockSync.setStock(GsonHelper.getInteger(json, "status"));
+    return spuStockSync;
+  }
+}
diff --git a/mallinkService/src/main/java/com/iformall/douyin/web/json/TtSpuSyncAdapter.java b/mallinkService/src/main/java/com/iformall/douyin/web/json/TtSpuSyncAdapter.java
new file mode 100644
index 000000000..0fdc5d73f
--- /dev/null
+++ b/mallinkService/src/main/java/com/iformall/douyin/web/json/TtSpuSyncAdapter.java
@@ -0,0 +1,94 @@
+/*
+ * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved.
+ *
+ * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended
+ * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction
+ * arose from modification of the original source, or other redistribution of this source
+ * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD.
+ */
+package com.iformall.douyin.web.json;
+
+import com.alibaba.fastjson.JSONArray;
+import com.google.gson.*;
+import com.iformall.douyin.web.bean.TtSpuSync;
+import com.iformall.douyin.web.bean.TtSupplierSync;
+import me.chanjar.weixin.common.util.json.GsonHelper;
+
+import java.lang.reflect.Type;
+import java.util.Arrays;
+import java.util.Collections;
+
+
+/**
+ * @author Daniel Qian
+ */
+public class TtSpuSyncAdapter implements JsonSerializer, JsonDeserializer {
+
+  @Override
+  public JsonElement serialize(TtSpuSync spuSync, Type typeOfSrc, JsonSerializationContext context) {
+    return convertToJson(spuSync);
+  }
+
+  protected JsonObject convertToJson(TtSpuSync spuSync) {
+    JsonObject json = new JsonObject();
+    if(spuSync.getEntryInfo() != null){
+      json.addProperty("entry_info", spuSync.getEntryInfo().toString());
+    }
+    if(spuSync.getFrontCategoryTag() != null && !spuSync.getFrontCategoryTag().isEmpty()){
+      json.addProperty("front_category_tag", new JSONArray(Collections.singletonList(spuSync.getFrontCategoryTag())).toJSONString());
+    }
+    json.addProperty("recommend_word", spuSync.getRecommendWord());
+    json.addProperty("sort_weight", spuSync.getSortWeight());
+    if(spuSync.getSupplierExtIdList() != null && !spuSync.getSupplierExtIdList().isEmpty()){
+      json.addProperty("supplier_ext_id_list", new JSONArray(Collections.singletonList(spuSync.getSupplierExtIdList())).toJSONString());
+    }
+    json.addProperty("price", spuSync.getPrice());
+    json.addProperty("spu_ext_id", spuSync.getSpuExtId());
+    if(spuSync.getImageList() != null && !spuSync.getImageList().isEmpty()){
+      json.addProperty("image_list", new JSONArray(Collections.singletonList(spuSync.getImageList())).toJSONString());
+    }
+    json.addProperty("mp_settle_type", spuSync.getMpSettletype());
+    json.addProperty("origin_price", spuSync.getOriginPrice());
+    json.addProperty("take_rate", spuSync.getTakeRate());
+    if(spuSync.getAttribute() != null){
+      json.addProperty("attribute", spuSync.getAttribute().toString());
+    }
+    if(spuSync.getHighlights() != null){
+      json.addProperty("highlights", spuSync.getHighlights().toString());
+    }
+    json.addProperty("name", spuSync.getName());
+    json.addProperty("order_depends_on_date", spuSync.getOrderDependsOnDate());
+    json.addProperty("spu_type", spuSync.getSpuType());
+    json.addProperty("status", spuSync.getStatus());
+    json.addProperty("stock", spuSync.getStock());
+    return json;
+  }
+
+  @Override
+  public TtSpuSync deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
+    return convertFromJson(json.getAsJsonObject());
+  }
+
+  protected TtSpuSync convertFromJson(JsonObject json) {
+    TtSpuSync spuSync = new TtSpuSync();
+    spuSync.setEntryInfo(json.getAsJsonObject("entry_info"));
+    spuSync.setFrontCategoryTag(Arrays.asList(GsonHelper.getStringArray(json, "front_category_tag")));
+    spuSync.setRecommendWord(GsonHelper.getString(json, "recommend_word"));
+    spuSync.setSortWeight(GsonHelper.getInteger(json, "sort_weight"));
+    spuSync.setSupplierExtIdList(Arrays.asList(GsonHelper.getStringArray(json, "supplier_ext_id_list")));
+    spuSync.setPrice(GsonHelper.getInteger(json, "price"));
+    spuSync.setSpuExtId(GsonHelper.getString(json, "spu_ext_id"));
+    spuSync.setImageList(Arrays.asList(GsonHelper.getStringArray(json, "image_list")));
+    spuSync.setMpSettletype(GsonHelper.getInteger(json, "mp_settle_type"));
+    spuSync.setOriginPrice(GsonHelper.getInteger(json, "origin_price"));
+    spuSync.setTakeRate(GsonHelper.getInteger(json, "take_rate"));
+    spuSync.setAttribute(json.getAsJsonObject("attribute"));
+    spuSync.setHighlights(json.getAsJsonArray("highlights"));
+    spuSync.setName(GsonHelper.getString(json, "name"));
+    spuSync.setOrderDependsOnDate(GsonHelper.getBoolean(json, "order_depends_on_date"));
+    spuSync.setSpuType(GsonHelper.getInteger(json, "spu_type"));
+    spuSync.setStatus(GsonHelper.getInteger(json, "status"));
+    spuSync.setStock(GsonHelper.getInteger(json, "stock"));
+    return spuSync;
+  }
+}
diff --git a/mallinkService/src/main/java/com/iformall/douyin/web/json/TtSupplierMatchAdapter.java b/mallinkService/src/main/java/com/iformall/douyin/web/json/TtSupplierMatchAdapter.java
new file mode 100644
index 000000000..1e8fb23ed
--- /dev/null
+++ b/mallinkService/src/main/java/com/iformall/douyin/web/json/TtSupplierMatchAdapter.java
@@ -0,0 +1,72 @@
+/*
+ * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved.
+ *
+ * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended
+ * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction
+ * arose from modification of the original source, or other redistribution of this source
+ * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD.
+ */
+package com.iformall.douyin.web.json;
+
+import com.google.gson.*;
+import com.iformall.douyin.web.bean.TtSupplierMatch;
+import com.iformall.douyin.web.bean.TtSupplierMatchList;
+import me.chanjar.weixin.common.util.json.GsonHelper;
+
+import java.lang.reflect.Type;
+
+
+/**
+ * @author Daniel Qian
+ */
+public class TtSupplierMatchAdapter implements JsonSerializer, JsonDeserializer {
+
+  @Override
+  public JsonElement serialize(TtSupplierMatch match, Type typeOfSrc, JsonSerializationContext context) {
+    return convertToJson(match);
+  }
+
+  protected JsonObject convertToJson(TtSupplierMatch match) {
+    JsonObject json = new JsonObject();
+    json.addProperty("city", match.getCity());
+    json.addProperty("latitude", match.getLatitude());
+    json.addProperty("longitude", match.getLongitude());
+    json.addProperty("amap_id", match.getAmapId());
+    json.addProperty("extra", match.getExtra());
+    json.addProperty("poi_id", match.getPoiId());
+    json.addProperty("poi_name", match.getPoiName());
+    json.addProperty("province", match.getProvince());
+    json.addProperty("supplier_ext_id", match.getSupplierExtId());
+    json.addProperty("address", match.getAddress());
+
+    return json;
+  }
+
+  @Override
+  public TtSupplierMatch deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
+    return convertFromJson(json.getAsJsonObject());
+  }
+
+  protected TtSupplierMatch convertFromJson(JsonObject json) {
+    TtSupplierMatch match = new TtSupplierMatch();
+    match.setCity(GsonHelper.getString(json, "city"));
+    match.setLatitude(GsonHelper.getFloat(json, "latitude"));
+    match.setLongitude(GsonHelper.getFloat(json, "longitude"));
+    match.setAmapId(GsonHelper.getString(json, "amap_id"));
+    match.setExtra(GsonHelper.getString(json, "extra"));
+    match.setPoiId(GsonHelper.getString(json, "poi_id"));
+    match.setPoiName(GsonHelper.getString(json, "poi_name"));
+    match.setProvince(GsonHelper.getString(json, "province"));
+    match.setSupplierExtId(GsonHelper.getString(json, "supplier_ext_id"));
+    match.setAddress(GsonHelper.getString(json, "address"));
+
+    match.setMatchStatus(GsonHelper.getInteger(json, "match_status"));
+    match.setMismatchStatusDesc(GsonHelper.getString(json,"mismatch_status_desc"));
+
+    match.setStatus(GsonHelper.getInteger(json, "status"));
+    match.setTaskId(GsonHelper.getString(json,"task_id"));
+
+    match.setIsSuccess(GsonHelper.getInteger(json, "is_success"));
+    return match;
+  }
+}
diff --git a/mallinkService/src/main/java/com/iformall/douyin/web/json/TtSupplierMatchListAdapter.java b/mallinkService/src/main/java/com/iformall/douyin/web/json/TtSupplierMatchListAdapter.java
new file mode 100644
index 000000000..1843409a3
--- /dev/null
+++ b/mallinkService/src/main/java/com/iformall/douyin/web/json/TtSupplierMatchListAdapter.java
@@ -0,0 +1,88 @@
+/*
+ * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved.
+ *
+ * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended
+ * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction
+ * arose from modification of the original source, or other redistribution of this source
+ * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD.
+ */
+package com.iformall.douyin.web.json;
+
+import com.google.gson.*;
+import com.iformall.douyin.web.bean.TtSupplierMatch;
+import com.iformall.douyin.web.bean.TtSupplierMatchList;
+
+import java.lang.reflect.Type;
+
+
+/**
+ * @author Daniel Qian
+ */
+public class TtSupplierMatchListAdapter implements JsonSerializer, JsonDeserializer {
+
+  @Override
+  public JsonElement serialize(TtSupplierMatchList matchList, Type typeOfSrc, JsonSerializationContext context) {
+    JsonObject json = new JsonObject();
+
+    JsonArray matchArray = new JsonArray();
+    for (TtSupplierMatch match : matchList.getMatchDataList()) {
+//      JsonObject matchJson = convertToJson(match);
+//      matchArray.add(matchJson);
+        JsonElement jsonElement = TtWebGsonBuilder.create().toJsonTree(match);
+        matchArray.add(jsonElement);
+    }
+    json.add("match_data_list", matchArray);
+    return json;
+  }
+
+//  protected JsonObject convertToJson(TtSupplierMatch match) {
+//    JsonObject json = new JsonObject();
+//    json.addProperty("city", match.getCity());
+//    json.addProperty("latitude", match.getLatitude());
+//    json.addProperty("longitude", match.getLongitude());
+//    json.addProperty("amap_id", match.getAmapId());
+//    json.addProperty("extra", match.getExtra());
+//    json.addProperty("poi_id", match.getPoiId());
+//    json.addProperty("poi_name", match.getPoiName());
+//    json.addProperty("province", match.getProvince());
+//    json.addProperty("supplier_ext_id", match.getSupplierExtId());
+//    json.addProperty("address", match.getAddress());
+//
+//    return json;
+//  }
+
+  @Override
+  public TtSupplierMatchList deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
+    TtSupplierMatchList matchList = new TtSupplierMatchList();
+    JsonArray matchListJson = json.getAsJsonObject().get("match_data_list").getAsJsonArray();
+    for (int i = 0; i < matchListJson.size(); i++) {
+      JsonObject matchJson = matchListJson.get(i).getAsJsonObject();
+//      TtSupplierMatch match = convertFromJson(matchJson);
+        TtSupplierMatch match = TtWebGsonBuilder.create().fromJson(matchJson, TtSupplierMatch.class);
+        matchList.getMatchDataList().add(match);
+    }
+    return matchList;
+  }
+
+//  protected TtSupplierMatch convertFromJson(JsonObject json) {
+//    TtSupplierMatch match = new TtSupplierMatch();
+//    match.setCity(GsonHelper.getString(json, "city"));
+//    match.setLatitude(GsonHelper.getFloat(json, "latitude"));
+//    match.setLongitude(GsonHelper.getFloat(json, "longitude"));
+//    match.setAmapId(GsonHelper.getString(json, "amap_id"));
+//    match.setExtra(GsonHelper.getString(json, "extra"));
+//    match.setPoiId(GsonHelper.getString(json, "poi_id"));
+//    match.setPoiName(GsonHelper.getString(json, "poi_name"));
+//    match.setProvince(GsonHelper.getString(json, "province"));
+//    match.setSupplierExtId(GsonHelper.getString(json, "supplier_ext_id"));
+//    match.setAddress(GsonHelper.getString(json, "address"));
+//
+//    match.setMatchStatus(GsonHelper.getInteger(json, "match_status"));
+//    match.setMismatchStatusDesc(GsonHelper.getString(json,"mismatch_status_desc"));
+//
+//    match.setStatus(GsonHelper.getInteger(json, "status"));
+//    match.setTaskId(GsonHelper.getString(json,"task_id"));
+//    return match;
+//  }
+
+}
diff --git a/mallinkService/src/main/java/com/iformall/douyin/web/json/TtSupplierSyncAdapter.java b/mallinkService/src/main/java/com/iformall/douyin/web/json/TtSupplierSyncAdapter.java
new file mode 100644
index 000000000..d699ea16c
--- /dev/null
+++ b/mallinkService/src/main/java/com/iformall/douyin/web/json/TtSupplierSyncAdapter.java
@@ -0,0 +1,108 @@
+/*
+ * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved.
+ *
+ * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended
+ * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction
+ * arose from modification of the original source, or other redistribution of this source
+ * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD.
+ */
+package com.iformall.douyin.web.json;
+
+import com.alibaba.fastjson.JSONArray;
+import com.google.gson.*;
+import com.iformall.douyin.web.bean.TtSupplierMatch;
+import com.iformall.douyin.web.bean.TtSupplierSync;
+import me.chanjar.weixin.common.util.json.GsonHelper;
+
+import java.lang.reflect.Type;
+import java.util.Arrays;
+import java.util.Collections;
+
+
+/**
+ * @author Daniel Qian
+ */
+public class TtSupplierSyncAdapter implements JsonSerializer, JsonDeserializer {
+
+  @Override
+  public JsonElement serialize(TtSupplierSync supplierSync, Type typeOfSrc, JsonSerializationContext context) {
+    return convertToJson(supplierSync);
+  }
+
+  protected JsonObject convertToJson(TtSupplierSync supplierSync) {
+    JsonObject json = new JsonObject();
+    json.addProperty("contact_phone", supplierSync.getContactPhone());
+    json.addProperty("contact_tel", supplierSync.getContactTel());
+    if(supplierSync.getImages() != null && !supplierSync.getImages().isEmpty()){
+      json.addProperty("images", new JSONArray(Collections.singletonList(supplierSync.getImages())).toJSONString());
+    }
+    json.addProperty("merchant_uid", supplierSync.getMerchantUid());
+    if(supplierSync.getServiceProvider() != null){
+      json.addProperty("service_provider", supplierSync.getServiceProvider().toString());
+    }
+    json.addProperty("supplier_ext_id", supplierSync.getSupplierExtId());
+    if(supplierSync.getTags() != null && !supplierSync.getTags().isEmpty()){
+      json.addProperty("tags", new JSONArray(Collections.singletonList(supplierSync.getTags())).toJSONString());
+    }
+    json.addProperty("latitude", supplierSync.getLatitude());
+    json.addProperty("status", supplierSync.getStatus());
+    json.addProperty("type_code", supplierSync.getTypeCode());
+    json.addProperty("type_name", supplierSync.getTypeName());
+    json.addProperty("address", supplierSync.getAddress());
+    json.addProperty("avg_cost", supplierSync.getAvgCost());
+    if(supplierSync.getCustomerInfo() != null ){
+      json.addProperty("customer_info", supplierSync.getCustomerInfo().toString());
+    }
+    json.addProperty("description", supplierSync.getDescription());
+    json.addProperty("name", supplierSync.getName());
+    if(supplierSync.getRecommends() != null){
+      json.addProperty("recommends", supplierSync.getRecommends().toString());
+    }
+    if(supplierSync.getServices() != null){
+      json.addProperty("services", supplierSync.getServices().toString());
+    }
+    if(supplierSync.getAttributes() != null){
+      json.addProperty("attributes", supplierSync.getAttributes().toString());
+    }
+    json.addProperty("longitude", supplierSync.getLongitude());
+    if(supplierSync.getOpenTime() != null && !supplierSync.getOpenTime().isEmpty()){
+      json.addProperty("open_time", new JSONArray(Collections.singletonList(supplierSync.getOpenTime())).toJSONString());
+    }
+    json.addProperty("poi_id", supplierSync.getPoiId());
+    json.addProperty("type", supplierSync.getType());
+    return json;
+  }
+
+  @Override
+  public TtSupplierSync deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
+    return convertFromJson(json.getAsJsonObject());
+  }
+
+  protected TtSupplierSync convertFromJson(JsonObject json) {
+    TtSupplierSync supplierSync = new TtSupplierSync();
+    supplierSync.setContactPhone(GsonHelper.getString(json, "contact_phone"));
+    supplierSync.setContactTel(GsonHelper.getString(json, "contact_tel"));
+    supplierSync.setImages(Arrays.asList(GsonHelper.getStringArray(json, "images")));
+    supplierSync.setMerchantUid(GsonHelper.getString(json, "merchant_uid"));
+    supplierSync.setServiceProvider(json.getAsJsonObject("service_provider"));
+    supplierSync.setSupplierExtId(GsonHelper.getString(json, "supplier_ext_id"));
+    supplierSync.setTags(Arrays.asList(GsonHelper.getStringArray(json, "tags")));
+    supplierSync.setLatitude(GsonHelper.getString(json, "latitude"));
+    supplierSync.setStatus(GsonHelper.getInteger(json, "status"));
+    supplierSync.setTypeCode(GsonHelper.getString(json, "type_code"));
+    supplierSync.setTypeName(GsonHelper.getString(json, "type_name"));
+    supplierSync.setAddress(GsonHelper.getString(json,"address"));
+    supplierSync.setAvgCost(GsonHelper.getInteger(json, "avg_cost"));
+    supplierSync.setCustomerInfo(json.getAsJsonObject("customer_info"));
+    supplierSync.setDescription(GsonHelper.getString(json, "description"));
+    supplierSync.setName(GsonHelper.getString(json, "name"));
+    supplierSync.setRecommends(json.getAsJsonArray("recommends"));
+    supplierSync.setServices(json.getAsJsonArray("services"));
+    supplierSync.setAttributes(json.getAsJsonObject("attributes"));
+    supplierSync.setLongitude(GsonHelper.getString(json, "longitude"));
+    supplierSync.setOpenTime(Arrays.asList(GsonHelper.getStringArray(json, "open_time")));
+    supplierSync.setPoiId(GsonHelper.getString(json,"poi_id"));
+    supplierSync.setType(GsonHelper.getInteger(json, "type"));
+    return supplierSync;
+  }
+}
diff --git a/mallinkService/src/main/java/com/iformall/douyin/web/json/TtSupplierSyncQueryAdapter.java b/mallinkService/src/main/java/com/iformall/douyin/web/json/TtSupplierSyncQueryAdapter.java
new file mode 100644
index 000000000..abf0c777f
--- /dev/null
+++ b/mallinkService/src/main/java/com/iformall/douyin/web/json/TtSupplierSyncQueryAdapter.java
@@ -0,0 +1,124 @@
+/*
+ * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved.
+ *
+ * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended
+ * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction
+ * arose from modification of the original source, or other redistribution of this source
+ * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD.
+ */
+package com.iformall.douyin.web.json;
+
+import com.google.gson.*;
+import com.iformall.douyin.web.bean.*;
+import me.chanjar.weixin.common.util.json.GsonHelper;
+
+import java.lang.reflect.Type
+
+
+/**
+ * @author Daniel Qian
+ */
+public class TtSupplierSyncQueryAdapter implements JsonSerializer, JsonDeserializer {
+
+  @Override
+  public JsonElement serialize(TtSupplierSyncQuery syncQuery, Type typeOfSrc, JsonSerializationContext context) {
+    JsonObject json = new JsonObject();
+//    json.add("data_detail", convertToJson(syncQuery.getDataDetail()));
+    JsonElement jsonElement = TtWebGsonBuilder.create().toJsonTree(syncQuery.getDataDetail());
+    json.add("data_detail", jsonElement);
+
+    JsonObject statusJson = new JsonObject();
+    statusJson.addProperty("last_sync_status",syncQuery.getSyncStatus().getLastSyncStatus());
+    statusJson.addProperty("fail_reason",syncQuery.getSyncStatus().getFailReason());
+    json.add("sync_status",statusJson);
+    return json;
+  }
+
+//  protected JsonObject convertToJson(TtSupplierSync supplierSync) {
+//    JsonObject json = new JsonObject();
+//    json.addProperty("contact_phone", supplierSync.getContactPhone());
+//    json.addProperty("contact_tel", supplierSync.getContactTel());
+//    if(supplierSync.getImages() != null && !supplierSync.getImages().isEmpty()){
+//      json.addProperty("images", new JSONArray(Collections.singletonList(supplierSync.getImages())).toJSONString());
+//    }
+//    json.addProperty("merchant_uid", supplierSync.getMerchantUid());
+//    if(supplierSync.getServiceProvider() != null){
+//      json.addProperty("service_provider", supplierSync.getServiceProvider().toString());
+//    }
+//    json.addProperty("supplier_ext_id", supplierSync.getSupplierExtId());
+//    if(supplierSync.getTags() != null && !supplierSync.getTags().isEmpty()){
+//      json.addProperty("tags", new JSONArray(Collections.singletonList(supplierSync.getTags())).toJSONString());
+//    }
+//    json.addProperty("latitude", supplierSync.getLatitude());
+//    json.addProperty("status", supplierSync.getStatus());
+//    json.addProperty("type_code", supplierSync.getTypeCode());
+//    json.addProperty("type_name", supplierSync.getTypeName());
+//    json.addProperty("address", supplierSync.getAddress());
+//    json.addProperty("avg_cost", supplierSync.getAvgCost());
+//    if(supplierSync.getCustomerInfo() != null ){
+//      json.addProperty("customer_info", supplierSync.getCustomerInfo().toString());
+//    }
+//    json.addProperty("description", supplierSync.getDescription());
+//    json.addProperty("name", supplierSync.getName());
+//    if(supplierSync.getRecommends() != null){
+//      json.addProperty("recommends", supplierSync.getRecommends().toString());
+//    }
+//    if(supplierSync.getServices() != null){
+//      json.addProperty("services", supplierSync.getServices().toString());
+//    }
+//    if(supplierSync.getAttributes() != null){
+//      json.addProperty("attributes", supplierSync.getAttributes().toString());
+//    }
+//    json.addProperty("longitude", supplierSync.getLongitude());
+//    if(supplierSync.getOpenTime() != null && !supplierSync.getOpenTime().isEmpty()){
+//      json.addProperty("open_time", new JSONArray(Collections.singletonList(supplierSync.getOpenTime())).toJSONString());
+//    }
+//    json.addProperty("poi_id", supplierSync.getPoiId());
+//    json.addProperty("type", supplierSync.getType());
+//    return json;
+//  }
+
+  @Override
+  public TtSupplierSyncQuery deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
+    TtSupplierSyncQuery syncQuery = new TtSupplierSyncQuery();
+    JsonObject syncObject = json.getAsJsonObject().get("data_detail").getAsJsonObject();
+//    syncQuery.setDataDetail(convertFromJson(syncObject));
+    TtSupplierSync ttSupplierSync = TtWebGsonBuilder.create().fromJson(syncObject, TtSupplierSync.class);
+    syncQuery.setDataDetail(ttSupplierSync);
+    JsonObject statusObject = json.getAsJsonObject().get("sync_status").getAsJsonObject();
+    TtSupplierSyncStatus syncStatus = new TtSupplierSyncStatus();
+    syncStatus.setLastSyncStatus(GsonHelper.getInteger(statusObject,"last_sync_status"));
+    syncStatus.setFailReason(GsonHelper.getString(statusObject,"fail_reason"));
+    syncQuery.setSyncStatus(syncStatus);
+    return syncQuery;
+  }
+
+//  protected TtSupplierSync convertFromJson(JsonObject json) {
+//    TtSupplierSync supplierSync = new TtSupplierSync();
+//    supplierSync.setContactPhone(GsonHelper.getString(json, "contact_phone"));
+//    supplierSync.setContactTel(GsonHelper.getString(json, "contact_tel"));
+//    supplierSync.setImages(Arrays.asList(GsonHelper.getStringArray(json, "images")));
+//    supplierSync.setMerchantUid(GsonHelper.getString(json, "merchant_uid"));
+//    supplierSync.setServiceProvider(json.getAsJsonObject("service_provider"));
+//    supplierSync.setSupplierExtId(GsonHelper.getString(json, "supplier_ext_id"));
+//    supplierSync.setTags(Arrays.asList(GsonHelper.getStringArray(json, "tags")));
+//    supplierSync.setLatitude(GsonHelper.getString(json, "latitude"));
+//    supplierSync.setStatus(GsonHelper.getInteger(json, "status"));
+//    supplierSync.setTypeCode(GsonHelper.getString(json, "type_code"));
+//    supplierSync.setTypeName(GsonHelper.getString(json, "type_name"));
+//    supplierSync.setAddress(GsonHelper.getString(json,"address"));
+//    supplierSync.setAvgCost(GsonHelper.getInteger(json, "avg_cost"));
+//    supplierSync.setCustomerInfo(json.getAsJsonObject("customer_info"));
+//    supplierSync.setDescription(GsonHelper.getString(json, "description"));
+//    supplierSync.setName(GsonHelper.getString(json, "name"));
+//    supplierSync.setRecommends(json.getAsJsonArray("recommends"));
+//    supplierSync.setServices(json.getAsJsonArray("services"));
+//    supplierSync.setAttributes(json.getAsJsonObject("attributes"));
+//    supplierSync.setLongitude(GsonHelper.getString(json, "longitude"));
+//    supplierSync.setOpenTime(Arrays.asList(GsonHelper.getStringArray(json, "open_time")));
+//    supplierSync.setPoiId(GsonHelper.getString(json,"poi_id"));
+//    supplierSync.setType(GsonHelper.getInteger(json, "type"));
+//    return supplierSync;
+//  }
+
+}
diff --git a/mallinkService/src/main/java/com/iformall/douyin/web/json/TtWebGsonBuilder.java b/mallinkService/src/main/java/com/iformall/douyin/web/json/TtWebGsonBuilder.java
new file mode 100644
index 000000000..24d57abb7
--- /dev/null
+++ b/mallinkService/src/main/java/com/iformall/douyin/web/json/TtWebGsonBuilder.java
@@ -0,0 +1,30 @@
+package com.iformall.douyin.web.json;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.iformall.douyin.web.bean.*;
+
+/**
+ * @author Binary Wang
+ */
+public class TtWebGsonBuilder {
+  private static final GsonBuilder INSTANCE = new GsonBuilder();
+
+  static {
+    INSTANCE.disableHtmlEscaping();
+    INSTANCE.registerTypeAdapter(TtSupplierMatchList.class, new TtSupplierMatchListAdapter());
+    INSTANCE.registerTypeAdapter(TtSupplierMatch.class, new TtSupplierMatchAdapter());
+    INSTANCE.registerTypeAdapter(TtSupplierSync.class, new TtSupplierSyncAdapter());
+    INSTANCE.registerTypeAdapter(TtSupplierSyncQuery.class, new TtSupplierSyncQueryAdapter());
+    INSTANCE.registerTypeAdapter(TtSpuSync.class, new TtSpuSyncAdapter());
+    INSTANCE.registerTypeAdapter(TtSpuStatusSync.class, new TtSpuStatusSyncAdapter());
+    INSTANCE.registerTypeAdapter(TtSpuGet.class, new TtSpuGetAdapter());
+    INSTANCE.registerTypeAdapter(TtSpuGetResult.class, new TtSpuGetResultAdapter());
+    INSTANCE.registerTypeAdapter(TtSpuStockSync.class, new TtSpuStockSyncAdapter());
+  }
+
+  public static Gson create() {
+    return INSTANCE.create();
+  }
+
+}
diff --git a/mallinkService/src/main/java/com/iformall/service/impl/WxProfitSharingReceiverServiceImpl.java b/mallinkService/src/main/java/com/iformall/service/impl/WxProfitSharingReceiverServiceImpl.java
index 73fd5ae81..1c1200415 100644
--- a/mallinkService/src/main/java/com/iformall/service/impl/WxProfitSharingReceiverServiceImpl.java
+++ b/mallinkService/src/main/java/com/iformall/service/impl/WxProfitSharingReceiverServiceImpl.java
@@ -275,7 +275,7 @@ public class WxProfitSharingReceiverServiceImpl implements WxProfitSharingReceiv
         AppAddSubMerchantResult balance_URLResult = appAddSubMerchant(appId,payAccountKey,merchant.getId().toString(),AppAddSubMerchantUrlType.Balance_URL);
         if(balance_URLResult.isSuccess()){
             wxProfitSharingReceiver.setReceiverAccount(improt_URLResult.getMerchantId());
-            wxProfitSharingReceiver.setTtBalanceUrl(balance_URLResult.getMerchantId());
+            wxProfitSharingReceiver.setTtBalanceUrl(balance_URLResult.getUrl());
         }else{
             logger.error("获取余额页面 error{}"+improt_URLResult.getMsg());
         }
diff --git a/mallinkService/src/main/java/com/iformall/utils/MaUtil.java b/mallinkService/src/main/java/com/iformall/utils/MaUtil.java
index ff46f09ef..fcf0fbe93 100644
--- a/mallinkService/src/main/java/com/iformall/utils/MaUtil.java
+++ b/mallinkService/src/main/java/com/iformall/utils/MaUtil.java
@@ -15,6 +15,10 @@ import com.iformall.douyin.miniapp.api.impl.TtMaServiceImpl;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 
+import com.iformall.douyin.web.api.TtWebService;
+import com.iformall.douyin.web.api.impl.TtWebServiceImpl;
+import com.iformall.douyin.web.config.TtWebDefaultConfigImpl;
+import com.iformall.douyin.web.enums.TtWebApiBeginEnum;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.stereotype.Service;
 
@@ -30,6 +34,9 @@ public class MaUtil {
 	
 	private static Map wxMaServiceMap = new ConcurrentHashMap();
 	private static Map wxMaAppServiceKeyMap = new ConcurrentHashMap();
+
+    private static Map webServiceMap = new ConcurrentHashMap();
+    private static Map webServiceKeyMap = new ConcurrentHashMap();
 	
     public WxMaService getWeappService(WxAppinfo appinfo) {
         WxMaDefaultConfigImpl config = new WxMaDefaultConfigImpl();
@@ -138,4 +145,45 @@ public class MaUtil {
         }
         return service;
     }
+
+    public TtWebService getTtWebService(WxAppinfo appinfo) {
+        TtWebDefaultConfigImpl config = new TtWebDefaultConfigImpl();
+        config.setAppid(appinfo.getAppId());
+        config.setSecret(appinfo.getSecret());
+        config.setToken(appinfo.getToken());
+        config.setAesKey(appinfo.getAesKey());
+        config.setMsgDataFormat(appinfo.getMsgDataFormat());
+        if (StringUtils.isNotBlank(appinfo.getAccessToken())) {
+            config.setAccessToken(appinfo.getAccessToken());
+            config.setExpiresTime(appinfo.getLastTokenTime().getTime()+appinfo.getExpiresIn()*1000);
+        }
+        config.setApacheHttpClientBuilder(FmHttpClientBuilder.get());
+
+        //默认抖音
+        config.setApiBegin(TtWebApiBeginEnum.TT);
+
+        String key = appinfo.getAppId()+String.valueOf(config.getExpiresTime());
+        TtWebService service = webServiceMap.get(key);
+        if ( null == service ) {
+            synchronized("getTtwebServiceBlock"+appinfo.getAppId()) {
+                service = webServiceMap.get(key);
+                if (null == service) {
+                    //删除掉之前的
+                    String appServiceKey = webServiceKeyMap.get(appinfo.getAppId());
+                    if (!StringUtils.isBlank(appServiceKey)) {
+                        if (webServiceMap.containsKey(appServiceKey)) {
+                            webServiceMap.remove(appServiceKey);
+                        }
+                        webServiceKeyMap.remove(appinfo.getAppId());
+                    }
+
+                    service = new TtWebServiceImpl();
+                    service.setTtWebConfig(config);
+                    webServiceMap.put(key, service);
+                    webServiceKeyMap.put(appinfo.getAppId(),key);
+                }
+            }
+        }
+        return service;
+    }
 }