Browse Source

:art: #1476 小程序云开发优化数据库CRUD操作的相关接口方法

skywsp 5 years ago
parent
commit
7a1863b6a1

+ 83 - 9
weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaCloudService.java

@@ -5,6 +5,7 @@ import com.google.gson.JsonArray;
 import me.chanjar.weixin.common.error.WxErrorException;
 
 import java.util.List;
+import java.util.Map;
 
 /**
  * 云开发相关接口.
@@ -32,6 +33,8 @@ public interface WxMaCloudService {
   String DATABASE_DELETE_URL = "https://api.weixin.qq.com/tcb/databasedelete";
   String DATABASE_ADD_URL = "https://api.weixin.qq.com/tcb/databaseadd";
 
+  String invokeCloudFunction(String name, String body) throws WxErrorException;
+
   /**
    * <pre>
    * 触发云函数。注意:HTTP API 途径触发云函数不包含用户信息。
@@ -50,6 +53,12 @@ public interface WxMaCloudService {
    */
   String invokeCloudFunction(String env, String name, String body) throws WxErrorException;
 
+  List<String> add(String collection, List list) throws WxErrorException;
+
+  String add(String collection, Object obj) throws WxErrorException;
+
+  JsonArray databaseAdd(String query) throws WxErrorException;
+
   /**
    * <pre>
    * 数据库插入记录
@@ -65,6 +74,10 @@ public interface WxMaCloudService {
    */
   JsonArray databaseAdd(String env, String query) throws WxErrorException;
 
+  Integer delete(String collection, String whereJson) throws WxErrorException;
+
+  int databaseDelete(String query) throws WxErrorException;
+
   /**
    * <pre>
    * 数据库删除记录
@@ -80,6 +93,10 @@ public interface WxMaCloudService {
    */
   int databaseDelete(String env, String query) throws WxErrorException;
 
+  WxCloudDatabaseUpdateResult update(String collection, String whereJson, String updateJson) throws WxErrorException;
+
+  WxCloudDatabaseUpdateResult databaseUpdate(String query) throws WxErrorException;
+
   /**
    * <pre>
    * 数据库更新记录
@@ -96,6 +113,29 @@ public interface WxMaCloudService {
   WxCloudDatabaseUpdateResult databaseUpdate(String env, String query) throws WxErrorException;
 
   /**
+   * db.collection('geo')
+   *   .where({
+   *     price: _.gt(10)
+   *   })
+   *   .orderBy('_id', 'asc')
+   *   .orderBy('price', 'desc')
+   *   .skip(1)
+   *   .limit(10)
+   *   .get()
+   * @param collection
+   * @param whereJson
+   * @param orderBy
+   * @param skip
+   * @param limit
+   * @return
+   * @throws WxErrorException
+   */
+  WxCloudDatabaseQueryResult query(String collection, String whereJson, Map<String, String> orderBy,
+                                   Integer skip, Integer limit) throws WxErrorException;
+
+  WxCloudDatabaseQueryResult databaseQuery(String query) throws WxErrorException;
+
+  /**
    * <pre>
    * 数据库查询记录
    *
@@ -110,6 +150,8 @@ public interface WxMaCloudService {
    */
   WxCloudDatabaseQueryResult databaseQuery(String env, String query) throws WxErrorException;
 
+  JsonArray databaseAggregate(String query) throws WxErrorException;
+
   /**
    * <pre>
    * 数据库聚合记录
@@ -125,6 +167,10 @@ public interface WxMaCloudService {
    */
   JsonArray databaseAggregate(String env, String query) throws WxErrorException;
 
+  Long count(String collection, String whereJson) throws WxErrorException;
+
+  Long databaseCount(String query) throws WxErrorException;
+
   /**
    * <pre>
    * 统计集合记录数或统计查询语句对应的结果记录数
@@ -140,6 +186,9 @@ public interface WxMaCloudService {
    */
   Long databaseCount(String env, String query) throws WxErrorException;
 
+  void updateIndex(String collectionName, List<WxCloudDatabaseCreateIndexRequest> createIndexes,
+                   List<String> dropIndexNames) throws WxErrorException;
+
   /**
    * <pre>
    * 变更数据库索引
@@ -157,11 +206,15 @@ public interface WxMaCloudService {
   void updateIndex(String env, String collectionName, List<WxCloudDatabaseCreateIndexRequest> createIndexes,
                    List<String> dropIndexNames) throws WxErrorException;
 
+  Long databaseMigrateImport(String collectionName, String filePath, int fileType,
+                             boolean stopOnError, int conflictMode) throws WxErrorException;
+
   /**
    * <pre>
    * 数据库导入
    *
-   * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseMigrateImport.html
+   * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseMigrateImport
+   * .html
    * 请求地址: POST https://api.weixin.qq.com/tcb/databasemigrateimport?access_token=ACCESS_TOKEN
    * </pre>
    *
@@ -177,11 +230,14 @@ public interface WxMaCloudService {
   Long databaseMigrateImport(String env, String collectionName, String filePath, int fileType, boolean stopOnError,
                              int conflictMode) throws WxErrorException;
 
+  Long databaseMigrateExport(String filePath, int fileType, String query) throws WxErrorException;
+
   /**
    * <pre>
    * 数据库导出
    *
-   * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseMigrateExport.html
+   * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseMigrateExport
+   * .html
    * 请求地址: POST https://api.weixin.qq.com/tcb/databasemigrateexport?access_token=ACCESS_TOKEN
    * </pre>
    *
@@ -194,11 +250,14 @@ public interface WxMaCloudService {
    */
   Long databaseMigrateExport(String env, String filePath, int fileType, String query) throws WxErrorException;
 
+  WxCloudCloudDatabaseMigrateQueryInfoResult databaseMigrateQueryInfo(Long jobId) throws WxErrorException;
+
   /**
    * <pre>
    *   数据库迁移状态查询
    *
-   *  文档地址:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseMigrateQueryInfo.html
+   *  文档地址:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database
+   *  /databaseMigrateQueryInfo.html
    *  请求地址:POST https://api.weixin.qq.com/tcb/databasemigratequeryinfo?access_token=ACCESS_TOKEN
    * </pre>
    *
@@ -209,6 +268,8 @@ public interface WxMaCloudService {
    */
   WxCloudCloudDatabaseMigrateQueryInfoResult databaseMigrateQueryInfo(String env, Long jobId) throws WxErrorException;
 
+  WxCloudUploadFileResult uploadFile(String path) throws WxErrorException;
+
   /**
    * <pre>
    * 获取文件上传链接
@@ -225,6 +286,8 @@ public interface WxMaCloudService {
    */
   WxCloudUploadFileResult uploadFile(String env, String path) throws WxErrorException;
 
+  WxCloudBatchDownloadFileResult batchDownloadFile(String[] fileIds, long[] maxAges) throws WxErrorException;
+
   /**
    * <pre>
    * 获取文件下载链接
@@ -242,6 +305,8 @@ public interface WxMaCloudService {
    */
   WxCloudBatchDownloadFileResult batchDownloadFile(String env, String[] fileIds, long[] maxAges) throws WxErrorException;
 
+  WxCloudBatchDeleteFileResult batchDeleteFile(String[] fileIds) throws WxErrorException;
+
   /**
    * <pre>
    * 删除文件
@@ -272,11 +337,14 @@ public interface WxMaCloudService {
    */
   WxCloudGetQcloudTokenResult getQcloudToken(long lifeSpan) throws WxErrorException;
 
+  void databaseCollectionAdd(String collectionName) throws WxErrorException;
+
   /**
    * <pre>
    * 新增集合
    *
-   * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseCollectionAdd.html
+   * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseCollectionAdd
+   * .html
    * 请求地址:POST https://api.weixin.qq.com/tcb/databasecollectionadd?access_token=ACCESS_TOKEN
    * </pre>
    *
@@ -286,11 +354,14 @@ public interface WxMaCloudService {
    */
   void databaseCollectionAdd(String env, String collectionName) throws WxErrorException;
 
+  void databaseCollectionDelete(String collectionName) throws WxErrorException;
+
   /**
    * <pre>
    * 删除集合
    *
-   * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseCollectionDelete.html
+   * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database
+   * /databaseCollectionDelete.html
    * 请求地址:POST https://api.weixin.qq.com/tcb/databasecollectionadd?access_token=ACCESS_TOKEN
    * </pre>
    *
@@ -300,17 +371,20 @@ public interface WxMaCloudService {
    */
   void databaseCollectionDelete(String env, String collectionName) throws WxErrorException;
 
+  WxCloudDatabaseCollectionGetResult databaseCollectionGet(Long limit, Long offset) throws WxErrorException;
+
   /**
    * <pre>
    * 获取特定云环境下集合信息
    *
-   * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseCollectionGet.html
+   * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseCollectionGet
+   * .html
    * 请求地址:POST https://api.weixin.qq.com/tcb/databasecollectionget?access_token=ACCESS_TOKEN
    * </pre>
    *
-   * @param env            云环境ID
-   * @param limit          获取数量限制,默认值:10
-   * @param offset         偏移量,默认值:0
+   * @param env    云环境ID
+   * @param limit  获取数量限制,默认值:10
+   * @param offset 偏移量,默认值:0
    * @return .
    * @throws WxErrorException .
    */

+ 244 - 7
weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaCloudServiceImpl.java

@@ -3,20 +3,24 @@ package cn.binarywang.wx.miniapp.api.impl;
 import cn.binarywang.wx.miniapp.api.WxMaCloudService;
 import cn.binarywang.wx.miniapp.api.WxMaService;
 import cn.binarywang.wx.miniapp.bean.cloud.*;
+import cn.binarywang.wx.miniapp.constant.WxMaConstants;
+import cn.binarywang.wx.miniapp.util.JoinerUtils;
+import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Lists;
 import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
 import com.google.gson.JsonParser;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.common.error.WxError;
 import me.chanjar.weixin.common.error.WxErrorException;
 import me.chanjar.weixin.common.util.json.WxGsonBuilder;
+import org.apache.commons.lang3.StringUtils;
 
 import java.io.Serializable;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 
 /**
  * 云开发相关接口实现类.
@@ -31,48 +35,225 @@ public class WxMaCloudServiceImpl implements WxMaCloudService {
   private final WxMaService wxMaService;
 
   @Override
+  public String invokeCloudFunction(String name, String body) throws WxErrorException {
+    String cloudEnv = this.wxMaService.getWxMaConfig().getCloudEnv();
+    return invokeCloudFunction(cloudEnv, name, body);
+  }
+
+  @Override
   public String invokeCloudFunction(String env, String name, String body) throws WxErrorException {
-    final String response = this.wxMaService.post(String.format(INVOKE_CLOUD_FUNCTION_URL, env, name), body);
+    String cloudEnv = this.wxMaService.getWxMaConfig().getCloudEnv();
+    final String response = this.wxMaService.post(String.format(INVOKE_CLOUD_FUNCTION_URL, cloudEnv, name), body);
     return JSON_PARSER.parse(response).getAsJsonObject().get("resp_data").getAsString();
   }
 
   @Override
+  public List<String> add(String collection, List list) throws WxErrorException {
+
+    String jsonData = WxMaGsonBuilder.create().toJson(list);
+    String query = JoinerUtils.blankJoiner.join(
+      "db.collection('", collection, "')",
+      ".add({data: ", jsonData, "})");
+
+    JsonObject params = new JsonObject();
+    params.addProperty("env", this.wxMaService.getWxMaConfig().getCloudEnv());
+    params.addProperty("query", query);
+
+    String responseContent = wxMaService.post(DATABASE_ADD_URL, params.toString());
+    JsonElement tmpJsonElement = JSON_PARSER.parse(responseContent);
+    JsonObject jsonObject = tmpJsonElement.getAsJsonObject();
+    if (jsonObject.get(WxMaConstants.ERRCODE).getAsInt() != 0) {
+      throw new WxErrorException(WxError.fromJson(responseContent));
+    }
+    JsonArray idArray = jsonObject.getAsJsonArray("id_list");
+    List<String> idList = new ArrayList<>();
+    Iterator<JsonElement> idIterator = idArray.iterator();
+    while (idIterator.hasNext()) {
+      JsonElement id = idIterator.next();
+      idList.add(id.getAsString());
+    }
+    return idList;
+  }
+
+  @Override
+  public String add(String collection, Object obj) throws WxErrorException {
+    String jsonData = WxMaGsonBuilder.create().toJson(obj);
+    String query = JoinerUtils.blankJoiner.join(
+      "db.collection('", collection, "')",
+      ".add({data: ", jsonData, "})");
+
+    JsonObject params = new JsonObject();
+    params.addProperty("env", this.wxMaService.getWxMaConfig().getCloudEnv());
+    params.addProperty("query", query);
+
+    String responseContent = wxMaService.post(DATABASE_ADD_URL, params.toString());
+    JsonElement tmpJsonElement = JSON_PARSER.parse(responseContent);
+    JsonObject jsonObject = tmpJsonElement.getAsJsonObject();
+    if (jsonObject.get(WxMaConstants.ERRCODE).getAsInt() != 0) {
+      throw new WxErrorException(WxError.fromJson(responseContent));
+    }
+    JsonArray idArray = jsonObject.getAsJsonArray("id_list");
+    return idArray.getAsString();
+  }
+
+  @Override
+  public JsonArray databaseAdd(String query) throws WxErrorException {
+    String cloudEnv = this.wxMaService.getWxMaConfig().getCloudEnv();
+    return databaseAdd(cloudEnv, query);
+  }
+
+  @Override
   public JsonArray databaseAdd(String env, String query) throws WxErrorException {
     String response = this.wxMaService.post(DATABASE_ADD_URL, ImmutableMap.of("env", env, "query", query));
     return JSON_PARSER.parse(response).getAsJsonObject().get("id_list").getAsJsonArray();
   }
 
   @Override
+  public Integer delete(String collection, String whereJson) throws WxErrorException {
+    String query = JoinerUtils.blankJoiner.join(
+      "db.collection('", collection, "')",
+      ".where(", whereJson, ").remove()");
+
+    JsonObject params = new JsonObject();
+    params.addProperty("env", this.wxMaService.getWxMaConfig().getCloudEnv());
+    params.addProperty("query", query);
+
+    String responseContent = wxMaService.post(DATABASE_DELETE_URL, params.toString());
+    JsonElement tmpJsonElement = JSON_PARSER.parse(responseContent);
+    JsonObject jsonObject = tmpJsonElement.getAsJsonObject();
+    if (jsonObject.get(WxMaConstants.ERRCODE).getAsInt() != 0) {
+      throw new WxErrorException(WxError.fromJson(responseContent));
+    }
+    Integer deletedNum = jsonObject.get("deleted").getAsInt();
+    return deletedNum;
+  }
+
+  @Override
+  public int databaseDelete(String query) throws WxErrorException {
+    String cloudEnv = this.wxMaService.getWxMaConfig().getCloudEnv();
+    return databaseDelete(cloudEnv, query);
+  }
+
+  @Override
   public int databaseDelete(String env, String query) throws WxErrorException {
     String response = this.wxMaService.post(DATABASE_DELETE_URL, ImmutableMap.of("env", env, "query", query));
     return JSON_PARSER.parse(response).getAsJsonObject().get("deleted").getAsInt();
   }
 
   @Override
+  public WxCloudDatabaseUpdateResult update(String collection, String whereJson, String updateJson) throws WxErrorException {
+    String query = JoinerUtils.blankJoiner.join(
+      "db.collection('", collection, "')",
+      ".where(", whereJson, ").update({data:", updateJson, " })");
+
+    JsonObject params = new JsonObject();
+    params.addProperty("env", this.wxMaService.getWxMaConfig().getCloudEnv());
+    params.addProperty("query", query);
+
+    String responseContent = wxMaService.post(DATABASE_UPDATE_URL, params.toString());
+    return WxGsonBuilder.create().fromJson(responseContent, WxCloudDatabaseUpdateResult.class);
+  }
+
+  @Override
+  public WxCloudDatabaseUpdateResult databaseUpdate(String query) throws WxErrorException {
+    String cloudEnv = this.wxMaService.getWxMaConfig().getCloudEnv();
+    return databaseUpdate(cloudEnv, query);
+  }
+
+  @Override
   public WxCloudDatabaseUpdateResult databaseUpdate(String env, String query) throws WxErrorException {
     String response = this.wxMaService.post(DATABASE_UPDATE_URL, ImmutableMap.of("env", env, "query", query));
     return WxGsonBuilder.create().fromJson(response, WxCloudDatabaseUpdateResult.class);
   }
 
   @Override
+  public WxCloudDatabaseQueryResult query(String collection, String whereJson, Map<String, String> orderBy,
+                                          Integer skip, Integer limit) throws WxErrorException {
+    if (StringUtils.isBlank(whereJson)) {
+      whereJson = "{}";
+    }
+    StringBuilder orderBySb = new StringBuilder();
+    if (null != orderBy && !orderBy.isEmpty()) {
+      orderBy.entrySet().forEach(
+        e -> orderBySb.append(".orderBy('").append(e.getKey()).append("', '").append(e.getValue()).append("')")
+      );
+    }
+    if (null == limit) {
+      limit = 100;
+    }
+    if (null == skip) {
+      skip = 0;
+    }
+    String query = JoinerUtils.blankJoiner.join(
+      "db.collection('", collection, "')",
+      ".where(", whereJson, ")", orderBySb.toString(), ".skip(", skip, ").limit(", limit, ").get()");
+
+    JsonObject params = new JsonObject();
+    params.addProperty("env", this.wxMaService.getWxMaConfig().getCloudEnv());
+    params.addProperty("query", query);
+
+    String responseContent = wxMaService.post(DATABASE_QUERY_URL, params.toString());
+    return WxGsonBuilder.create().fromJson(responseContent, WxCloudDatabaseQueryResult.class);
+  }
+
+  @Override
+  public WxCloudDatabaseQueryResult databaseQuery(String query) throws WxErrorException {
+    String cloudEnv = this.wxMaService.getWxMaConfig().getCloudEnv();
+    return databaseQuery(cloudEnv, query);
+  }
+
+  @Override
   public WxCloudDatabaseQueryResult databaseQuery(String env, String query) throws WxErrorException {
     String response = this.wxMaService.post(DATABASE_QUERY_URL, ImmutableMap.of("env", env, "query", query));
     return WxGsonBuilder.create().fromJson(response, WxCloudDatabaseQueryResult.class);
   }
 
   @Override
+  public JsonArray databaseAggregate(String query) throws WxErrorException {
+    String cloudEnv = this.wxMaService.getWxMaConfig().getCloudEnv();
+    return databaseAggregate(cloudEnv, query);
+  }
+
+  @Override
   public JsonArray databaseAggregate(String env, String query) throws WxErrorException {
     String response = this.wxMaService.post(DATABASE_AGGREGATE_URL, ImmutableMap.of("env", env, "query", query));
     return JSON_PARSER.parse(response).getAsJsonObject().get("data").getAsJsonArray();
   }
 
   @Override
+  public Long count(String collection, String whereJson) throws WxErrorException {
+    String query = JoinerUtils.blankJoiner.join(
+      "db.collection('", collection, "')",
+      ".where(", whereJson, ").count()");
+
+    JsonObject params = new JsonObject();
+    params.addProperty("env", this.wxMaService.getWxMaConfig().getCloudEnv());
+    params.addProperty("query", query);
+
+    String responseContent = wxMaService.post(DATABASE_COUNT_URL, params.toString());
+    return JSON_PARSER.parse(responseContent).getAsJsonObject().get("count").getAsLong();
+  }
+
+  @Override
+  public Long databaseCount(String query) throws WxErrorException {
+    String cloudEnv = this.wxMaService.getWxMaConfig().getCloudEnv();
+    return databaseCount(cloudEnv, query);
+  }
+
+  @Override
   public Long databaseCount(String env, String query) throws WxErrorException {
     String response = this.wxMaService.post(DATABASE_COUNT_URL, ImmutableMap.of("env", env, "query", query));
     return JSON_PARSER.parse(response).getAsJsonObject().get("count").getAsLong();
   }
 
   @Override
+  public void updateIndex(String collectionName, List<WxCloudDatabaseCreateIndexRequest> createIndexes,
+                          List<String> dropIndexNames) throws WxErrorException {
+    String cloudEnv = this.wxMaService.getWxMaConfig().getCloudEnv();
+    updateIndex(cloudEnv, collectionName, createIndexes, dropIndexNames);
+  }
+
+  @Override
   public void updateIndex(String env, String collectionName, List<WxCloudDatabaseCreateIndexRequest> createIndexes,
                           List<String> dropIndexNames) throws WxErrorException {
     List<Map<String, String>> dropIndexes = Lists.newArrayList();
@@ -87,6 +268,13 @@ public class WxMaCloudServiceImpl implements WxMaCloudService {
   }
 
   @Override
+  public Long databaseMigrateImport(String collectionName, String filePath, int fileType,
+                                    boolean stopOnError, int conflictMode) throws WxErrorException {
+    String cloudEnv = this.wxMaService.getWxMaConfig().getCloudEnv();
+    return databaseMigrateImport(cloudEnv, collectionName, filePath, fileType, stopOnError, conflictMode);
+  }
+
+  @Override
   public Long databaseMigrateImport(String env, String collectionName, String filePath, int fileType,
                                     boolean stopOnError, int conflictMode) throws WxErrorException {
     JsonObject params = new JsonObject();
@@ -102,6 +290,12 @@ public class WxMaCloudServiceImpl implements WxMaCloudService {
   }
 
   @Override
+  public Long databaseMigrateExport(String filePath, int fileType, String query) throws WxErrorException {
+    String cloudEnv = this.wxMaService.getWxMaConfig().getCloudEnv();
+    return databaseMigrateExport(cloudEnv, filePath, fileType, query);
+  }
+
+  @Override
   public Long databaseMigrateExport(String env, String filePath, int fileType, String query) throws WxErrorException {
     JsonObject params = new JsonObject();
     params.addProperty("env", env);
@@ -114,18 +308,37 @@ public class WxMaCloudServiceImpl implements WxMaCloudService {
   }
 
   @Override
+  public WxCloudCloudDatabaseMigrateQueryInfoResult databaseMigrateQueryInfo(Long jobId) throws WxErrorException {
+    String cloudEnv = this.wxMaService.getWxMaConfig().getCloudEnv();
+    return databaseMigrateQueryInfo(cloudEnv, jobId);
+  }
+
+  @Override
   public WxCloudCloudDatabaseMigrateQueryInfoResult databaseMigrateQueryInfo(String env, Long jobId) throws WxErrorException {
-    String response = this.wxMaService.post(DATABASE_MIGRATE_QUERY_INFO_URL, ImmutableMap.of("env", env, "job_id", jobId));
+    String response = this.wxMaService.post(DATABASE_MIGRATE_QUERY_INFO_URL, ImmutableMap.of("env", env, "job_id",
+      jobId));
     return WxGsonBuilder.create().fromJson(response, WxCloudCloudDatabaseMigrateQueryInfoResult.class);
   }
 
   @Override
+  public WxCloudUploadFileResult uploadFile(String path) throws WxErrorException {
+    String cloudEnv = this.wxMaService.getWxMaConfig().getCloudEnv();
+    return uploadFile(cloudEnv, path);
+  }
+
+  @Override
   public WxCloudUploadFileResult uploadFile(String env, String path) throws WxErrorException {
     String response = this.wxMaService.post(UPLOAD_FILE_URL, ImmutableMap.of("env", env, "path", path));
     return WxGsonBuilder.create().fromJson(response, WxCloudUploadFileResult.class);
   }
 
   @Override
+  public WxCloudBatchDownloadFileResult batchDownloadFile(String[] fileIds, long[] maxAges) throws WxErrorException {
+    String cloudEnv = this.wxMaService.getWxMaConfig().getCloudEnv();
+    return batchDownloadFile(cloudEnv, fileIds, maxAges);
+  }
+
+  @Override
   public WxCloudBatchDownloadFileResult batchDownloadFile(String env, String[] fileIds, long[] maxAges) throws WxErrorException {
     List<Map<String, Serializable>> fileList = Lists.newArrayList();
     int i = 0;
@@ -138,6 +351,12 @@ public class WxMaCloudServiceImpl implements WxMaCloudService {
   }
 
   @Override
+  public WxCloudBatchDeleteFileResult batchDeleteFile(String[] fileIds) throws WxErrorException {
+    String cloudEnv = this.wxMaService.getWxMaConfig().getCloudEnv();
+    return batchDeleteFile(cloudEnv, fileIds);
+  }
+
+  @Override
   public WxCloudBatchDeleteFileResult batchDeleteFile(String env, String[] fileIds) throws WxErrorException {
     String response = this.wxMaService.post(BATCH_DELETE_FILE_URL, ImmutableMap.of("env", env, "fileid_list", fileIds));
     return WxGsonBuilder.create().fromJson(response, WxCloudBatchDeleteFileResult.class);
@@ -150,13 +369,32 @@ public class WxMaCloudServiceImpl implements WxMaCloudService {
   }
 
   @Override
+  public void databaseCollectionAdd(String collectionName) throws WxErrorException {
+    String cloudEnv = this.wxMaService.getWxMaConfig().getCloudEnv();
+    databaseCollectionAdd(cloudEnv, collectionName);
+  }
+
+  @Override
   public void databaseCollectionAdd(String env, String collectionName) throws WxErrorException {
     this.wxMaService.post(DATABASE_COLLECTION_ADD_URL, ImmutableMap.of("env", env, "collection_name", collectionName));
   }
 
   @Override
+  public void databaseCollectionDelete(String collectionName) throws WxErrorException {
+    String cloudEnv = this.wxMaService.getWxMaConfig().getCloudEnv();
+    databaseCollectionDelete(cloudEnv, collectionName);
+  }
+
+  @Override
   public void databaseCollectionDelete(String env, String collectionName) throws WxErrorException {
-    this.wxMaService.post(DATABASE_COLLECTION_DELETE_URL, ImmutableMap.of("env", env, "collection_name", collectionName));
+    this.wxMaService.post(DATABASE_COLLECTION_DELETE_URL, ImmutableMap.of("env", env, "collection_name",
+      collectionName));
+  }
+
+  @Override
+  public WxCloudDatabaseCollectionGetResult databaseCollectionGet(Long limit, Long offset) throws WxErrorException {
+    String cloudEnv = this.wxMaService.getWxMaConfig().getCloudEnv();
+    return databaseCollectionGet(cloudEnv, limit, offset);
   }
 
   @Override
@@ -174,5 +412,4 @@ public class WxMaCloudServiceImpl implements WxMaCloudService {
     String response = this.wxMaService.post(DATABASE_COLLECTION_GET_URL, params);
     return WxGsonBuilder.create().fromJson(response, WxCloudDatabaseCollectionGetResult.class);
   }
-
 }

+ 4 - 0
weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/WxMaConfig.java

@@ -87,6 +87,10 @@ public interface WxMaConfig {
 
   String getAesKey();
 
+  String getOriginalId();
+
+  String getCloudEnv();
+
   String getMsgDataFormat();
 
   long getExpiresTime();

+ 37 - 17
weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/impl/WxMaDefaultConfigImpl.java

@@ -1,51 +1,53 @@
 package cn.binarywang.wx.miniapp.config.impl;
 
-import java.io.File;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantLock;
-
 import cn.binarywang.wx.miniapp.config.WxMaConfig;
 import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder;
 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 <a href="https://github.com/binarywang">Binary Wang</a>
  */
 public class WxMaDefaultConfigImpl implements WxMaConfig {
-  private volatile String msgDataFormat;
   protected volatile String appid;
-  private volatile String secret;
   protected volatile String token;
+  /**
+   * 小程序原始ID
+   */
+  protected volatile String originalId;
+  protected Lock accessTokenLock = new ReentrantLock();
+  /**
+   * 临时文件目录.
+   */
+  protected volatile File tmpDirFile;
+  private volatile String msgDataFormat;
+  private volatile String secret;
   private volatile String accessToken;
   private volatile String aesKey;
   private volatile long expiresTime;
-
+  /**
+   * 云环境ID
+   */
+  private volatile String cloudEnv;
   private volatile String httpProxyHost;
   private volatile int httpProxyPort;
   private volatile String httpProxyUsername;
   private volatile String httpProxyPassword;
-
   private volatile String jsapiTicket;
   private volatile long jsapiTicketExpiresTime;
-
   /**
    * 微信卡券的ticket单独缓存.
    */
   private volatile String cardApiTicket;
   private volatile long cardApiTicketExpiresTime;
-
-  protected Lock accessTokenLock = new ReentrantLock();
   private Lock jsapiTicketLock = new ReentrantLock();
   private Lock cardApiTicketLock = new ReentrantLock();
-
-  /**
-   * 临时文件目录.
-   */
-  protected volatile File tmpDirFile;
-
   private volatile ApacheHttpClientBuilder apacheHttpClientBuilder;
 
   /**
@@ -191,6 +193,24 @@ public class WxMaDefaultConfigImpl implements WxMaConfig {
   }
 
   @Override
+  public String getOriginalId() {
+    return originalId;
+  }
+
+  public void setOriginalId(String originalId) {
+    this.originalId = originalId;
+  }
+
+  @Override
+  public String getCloudEnv() {
+    return this.cloudEnv;
+  }
+
+  public void setCloudEnv(String cloudEnv) {
+    this.cloudEnv = cloudEnv;
+  }
+
+  @Override
   public String getMsgDataFormat() {
     return this.msgDataFormat;
   }

+ 253 - 0
weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/JoinerUtils.java

@@ -0,0 +1,253 @@
+/**
+ * Copyright (c) 2019,sunnybs.
+ * All Rights Reserved.
+ * <p>
+ * Project Name:yigou
+ */
+package cn.binarywang.wx.miniapp.util;
+
+import com.google.common.base.Joiner;
+
+/**
+ * ClassName: JoinerUtils <br/>
+ * Description: 字符串连接器 <br/>
+ * Date: 2019年10月18日 下午1:42:59 <br/>
+ *
+ * @author <a href="https://gitee.com/esunego_hunan/weixin-java-tools">wsp</a>
+ */
+
+public class JoinerUtils {
+    private static final String NULL = "null";
+
+    /**
+     * <p>
+     * 空白连接器,忽略null
+     * </p>
+     *
+     * <pre>
+     * JoinerUtils.blankJoiner.join("a", "b", "c");
+     * <b>result : </b>abc
+     * 
+     * JoinerUtils.blankJoiner.join("a", null, "c");
+     * <b>result : </b>ac
+     * </pre>
+     */
+    public static final Joiner blankJoiner = Joiner.on("").skipNulls();
+    /**
+     * <p>
+     * 空白连接器
+     * </p>
+     *
+     * <pre>
+     * JoinerUtils.blankJoinerWithNull.join("a", "b", "c");
+     * <b>result : </b>abc
+     * 
+     * JoinerUtils.blankJoinerWithNull.join("a", null, "c");
+     * <b>result : </b>anullc
+     * </pre>
+     */
+    public static final Joiner blankJoinerWithNull = Joiner.on("").useForNull(NULL);
+
+    /**
+     * <p>
+     * 空格连接器,忽略null
+     * </p>
+     *
+     * <pre>
+     * JoinerUtils.spaceJoiner.join("a", "b", "c");
+     * <b>result : </b>a b c
+     * 
+     * JoinerUtils.spaceJoiner.join("a", null, "c");
+     * <b>result : </b>a c
+     * </pre>
+     */
+    public static final Joiner spaceJoiner = Joiner.on(" ").skipNulls();
+    /**
+     * <p>
+     * 空格连接器
+     * </p>
+     *
+     * <pre>
+     * JoinerUtils.spaceJoinerWithNull.join("a", "b", "c");
+     * <b>result : </b>a b c
+     * 
+     * JoinerUtils.spaceJoinerWithNull.join("a", null, "c");
+     * <b>result : </b>a null c
+     * </pre>
+     */
+    public static final Joiner spaceJoinerWithNull = Joiner.on(" ").useForNull(NULL);
+
+  /**
+   * <p>
+   * 逗号分隔符连接器,忽略null
+   * </p>
+   *
+   * <pre>
+   * JoinerUtils.commaJoiner.join("a", "b", "c");
+   * <b>result : </b>a,b,c
+   *
+   * JoinerUtils.commaJoiner.join("a", null, "c");
+   * <b>result : </b>a,c
+   * </pre>
+   */
+  public static final Joiner commaJoiner = Joiner.on(",").skipNulls();
+  /**
+   * <p>
+   * 逗号分隔符连接器
+   * </p>
+   *
+   * <pre>
+   * JoinerUtils.commaJoinerWithNull.join("a", "b", "c");
+   * <b>result : </b>a,b,c
+   *
+   * JoinerUtils.commaJoinerWithNull.join("a", null, "c");
+   * <b>result : </b>a,null,c
+   * </pre>
+   */
+  public static final Joiner commaJoinerWithNull = Joiner.on(",").useForNull(NULL);
+
+  /**
+   * <p>
+   * 等号分隔符连接器,忽略null
+   * </p>
+   *
+   * <pre>
+   * JoinerUtils.equalJoiner.join("a", "b", "c");
+   * <b>result : </b>a=b=c
+   *
+   * JoinerUtils.equalJoiner.join("a", null, "c");
+   * <b>result : </b>a=c
+   * </pre>
+   */
+  public static final Joiner equalJoiner = Joiner.on("=").skipNulls();
+  /**
+   * <p>
+   * 等号分隔符连接器
+   * </p>
+   *
+   * <pre>
+   * JoinerUtils.equalJoinerWithNull.join("a", "b", "c");
+   * <b>result : </b>a=b=c
+   *
+   * JoinerUtils.equalJoinerWithNull.join("a", null, "c");
+   * <b>result : </b>a=null=c
+   * </pre>
+   */
+  public static final Joiner equalJoinerWithNull = Joiner.on("=").useForNull(NULL);
+
+    /**
+     * <p>
+     * 竖线分隔符连接器,忽略null
+     * </p>
+     *
+     * <pre>
+     * JoinerUtils.vLineJoiner.join("a", "b", "c");
+     * <b>result : </b>a|b|c
+     * 
+     * JoinerUtils.vLineJoiner.join("a", null, "c");
+     * <b>result : </b>a|c
+     * </pre>
+     */
+    public static final Joiner vLineJoiner = Joiner.on("|").skipNulls();
+    /**
+     * <p>
+     * 竖线分隔符连接器
+     * </p>
+     *
+     * <pre>
+     * JoinerUtils.vLineJoinerWithNull.join("a", "b", "c");
+     * <b>result : </b>a|b|c
+     * 
+     * JoinerUtils.vLineJoinerWithNull.join("a", null, "c");
+     * <b>result : </b>a|null|c
+     * </pre>
+     */
+    public static final Joiner vLineJoinerWithNull = Joiner.on("|").useForNull(NULL);
+
+    /**
+     * <p>
+     * 中横线分隔符连接器,忽略null
+     * </p>
+     *
+     * <pre>
+     * JoinerUtils.hLineJoiner.join("a", "b", "c");
+     * <b>result : </b>a-b-c
+     * 
+     * JoinerUtils.hLineJoiner.join("a", null, "c");
+     * <b>result : </b>a-c
+     * </pre>
+     */
+    public static final Joiner hLineJoiner = Joiner.on("-").skipNulls();
+    /**
+     * <p>
+     * 中横线分隔符连接器
+     * </p>
+     *
+     * <pre>
+     * JoinerUtils.hLineJoinerWithNull.join("a", "b", "c");
+     * <b>result : </b>a-b-c
+     * 
+     * JoinerUtils.hLineJoinerWithNull.join("a", null, "c");
+     * <b>result : </b>a-null-c
+     * </pre>
+     */
+    public static final Joiner hLineJoinerWithNull = Joiner.on("-").useForNull(NULL);
+
+    /**
+     * <p>
+     * 下划线分隔符连接器,忽略null
+     * </p>
+     *
+     * <pre>
+     * JoinerUtils.underlineJoiner.join("a", "b", "c");
+     * <b>result : </b>a_b_c
+     * 
+     * JoinerUtils.underlineJoiner.join("a", null, "c");
+     * <b>result : </b>a_c
+     * </pre>
+     */
+    public static final Joiner underlineJoiner = Joiner.on("_").skipNulls();
+    /**
+     * <p>
+     * 下划线分隔符连接器
+     * </p>
+     *
+     * <pre>
+     * JoinerUtils.underlineJoinerWithNull.join("a", "b", "c");
+     * <b>result : </b>a_b_c
+     * 
+     * JoinerUtils.underlineJoinerWithNull.join("a", null, "c");
+     * <b>result : </b>a_null_c
+     * </pre>
+     */
+    public static final Joiner underlineJoinerWithNull = Joiner.on("_").useForNull(NULL);
+
+    /**
+     * <p>
+     * 斜线分隔符连接器,忽略null
+     * </p>
+     *
+     * <pre>
+     * JoinerUtils.pathJoiner.join("a", "b", "c");
+     * <b>result : </b>a/b/c
+     * 
+     * JoinerUtils.pathJoiner.join("a", null, "c");
+     * <b>result : </b>a/c
+     * </pre>
+     */
+    public static final Joiner pathJoiner = Joiner.on("/").skipNulls();
+    /**
+     * <p>
+     * 斜线分隔符连接器
+     * </p>
+     *
+     * <pre>
+     * JoinerUtils.pathJoinerWithNull.join("a", "b", "c");
+     * <b>result : </b>a/b/c
+     * 
+     * JoinerUtils.pathJoinerWithNull.join("a", null, "c");
+     * <b>result : </b>a/null/c
+     * </pre>
+     */
+    public static final Joiner pathJoinerWithNull = Joiner.on("/").useForNull(NULL);
+}

+ 193 - 20
weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaCloudServiceImplTest.java

@@ -3,13 +3,19 @@ package cn.binarywang.wx.miniapp.api.impl;
 import cn.binarywang.wx.miniapp.api.WxMaService;
 import cn.binarywang.wx.miniapp.bean.cloud.*;
 import cn.binarywang.wx.miniapp.test.ApiTestModule;
+import com.google.common.collect.ImmutableSortedMap;
 import com.google.common.collect.Lists;
+import com.google.common.collect.Ordering;
 import com.google.gson.JsonArray;
 import com.google.inject.Inject;
 import me.chanjar.weixin.common.error.WxErrorException;
+import org.testng.annotations.BeforeTest;
 import org.testng.annotations.Guice;
 import org.testng.annotations.Test;
 
+import java.math.BigDecimal;
+import java.util.*;
+
 import static org.assertj.core.api.Assertions.assertThat;
 
 /**
@@ -20,18 +26,89 @@ import static org.assertj.core.api.Assertions.assertThat;
  */
 @Guice(modules = ApiTestModule.class)
 public class WxMaCloudServiceImplTest {
+
+  private static final String COLLECTION = "geo";
   @Inject
   private WxMaService wxMaService;
 
+  @BeforeTest
+  public void before() {
+    /**
+     * 用以解决:javax.net.ssl.SSLHandshakeException: PKIX path building failed
+     * 参考:https://www.cnblogs.com/cloudapps/p/5022544.html
+     */
+    String mpCert = ClassLoader.getSystemResource("wx-mp-jssecacerts").getPath();
+    String maCert = ClassLoader.getSystemResource("wx-ma-jssecacerts").getPath();
+    System.setProperty("javax.net.ssl.trustStore", mpCert + "," + maCert);
+    String property = System.getProperty("javax.net.ssl.trustStore");
+    System.out.println("javax.net.ssl.trustStore=" + property);
+  }
+
+
   @Test
   public void testInvokeCloudFunction() throws WxErrorException {
-    final String result = this.wxMaService.getCloudService().invokeCloudFunction("rcn", "login", "{}");
+    final String result = this.wxMaService.getCloudService().invokeCloudFunction("login", "{}");
     assertThat(result).isNotNull();
   }
 
   @Test
+  public void testAddList() throws WxErrorException {
+    List<Map> stuList = new ArrayList<>();
+
+    Map<String, Object> product1 = new HashMap<>();
+    product1.put("description", "item1");
+    product1.put("price", BigDecimal.valueOf(1.2));
+    product1.put("due", new Date());
+
+    /**
+     * 等价于new db.Geo.Point(113, 23)
+     * 参见:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-sdk-api/database/geo/Geo.Point.html
+     */
+    Map<String, Object> point = new HashMap<>();
+    point.put("type", "Point");
+    point.put("coordinates", new Integer[]{113, 23});
+
+    Map<String, Object> product2 = new HashMap<>();
+    product2.put("tags", new String[]{"cloud", "database"});
+    product2.put("location", point);
+    product2.put("done", false);
+
+    stuList.add(product1);
+    stuList.add(product2);
+    List<String> idList = this.wxMaService.getCloudService().add(COLLECTION, stuList);
+
+    System.out.println(idList.size());
+    assertThat(idList).isNotEmpty();
+  }
+
+  @Test
+  public void testAddObject() throws WxErrorException {
+    /**
+     * 等价于new db.Geo.Point(113, 23)
+     * 参见:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-sdk-api/database/geo/Geo.Point.html
+     */
+    Map<String, Object> point = new HashMap<>();
+    point.put("type", "Point");
+    point.put("coordinates", new Integer[]{113, 23});
+
+    Map<String, Object> product = new HashMap<>();
+    product.put("description", "item1");
+    product.put("price", BigDecimal.valueOf(1.2));
+    product.put("due", new Date());
+    product.put("tags", new String[]{"cloud", "database"});
+    product.put("location", point);
+    product.put("done", false);
+
+    String id = this.wxMaService.getCloudService().add(COLLECTION, product);
+
+    System.out.println(id);
+    assertThat(id).isNotBlank();
+  }
+
+
+  @Test
   public void testDatabaseAdd() throws WxErrorException {
-    JsonArray array = this.wxMaService.getCloudService().databaseAdd("rcn", "db.collection(\"geo\").add({\n" +
+    JsonArray array = this.wxMaService.getCloudService().databaseAdd("db.collection(\"geo\").add({\n" +
       "      data: [{\n" +
       "      description: \"item1\",\n" +
       "      due:\n" +
@@ -64,15 +141,58 @@ public class WxMaCloudServiceImplTest {
   }
 
   @Test
+  public void testDelete() throws WxErrorException {
+    StringBuilder whereParamSb = new StringBuilder();
+    whereParamSb.append("{")
+      // 等于
+      .append("_id: _.eq('79a2c43f5e7e9e8e001a120e494d51b8'),")
+      // in
+      .append("age: _.in([0, 1, 2, 3]),")
+      // 小于
+      .append("due: _.lt('Mar 29, 2020 8:47:07 AM'),")
+      // 存在属性
+      .append("price: _.exists(true)")
+      .append("}");
+
+    final int result = this.wxMaService.getCloudService().delete(
+      COLLECTION, whereParamSb.toString());
+    assertThat(result).isEqualTo(0);
+  }
+
+  @Test
   public void testDatabaseDelete() throws WxErrorException {
-    final int result = this.wxMaService.getCloudService().databaseDelete("rcn",
+    final int result = this.wxMaService.getCloudService().databaseDelete(
       "db.collection(\"geo\").doc(\"056120a7-c89e-4616-95bf-dfc9a11daa3b\").remove()");
     assertThat(result).isEqualTo(0);
   }
 
   @Test
+  public void testUpdate() throws WxErrorException {
+    StringBuilder whereParamSb = new StringBuilder();
+    whereParamSb.append("{")
+      // in
+      .append("age: _.in([0, 1, 2, 3]),")
+      // 小于
+      .append("due: _.lt('Mar 29, 2020 8:47:07 AM'),")
+      // 存在属性
+      .append("price: _.exists(true)")
+      .append("}");
+
+    StringBuilder updateSb = new StringBuilder();
+    updateSb.append("{age: _.inc(1)}");
+
+    final WxCloudDatabaseUpdateResult result = this.wxMaService.getCloudService().update(COLLECTION,
+      whereParamSb.toString(), updateSb.toString());
+
+    assertThat(result).isNotNull();
+    assertThat(result.getMatched()).isGreaterThan(0);
+    assertThat(result.getId()).isEmpty();
+    assertThat(result.getModified()).isGreaterThan(0);
+  }
+
+  @Test
   public void testDatabaseUpdate() throws WxErrorException {
-    final WxCloudDatabaseUpdateResult result = this.wxMaService.getCloudService().databaseUpdate("rcn",
+    final WxCloudDatabaseUpdateResult result = this.wxMaService.getCloudService().databaseUpdate(
       "db.collection(\"geo\").where({description:\"item1\"}).update({data:{age: _.inc(1)}})");
     assertThat(result).isNotNull();
     assertThat(result.getMatched()).isGreaterThan(0);
@@ -81,8 +201,42 @@ public class WxMaCloudServiceImplTest {
   }
 
   @Test
+  public void testQuery() throws WxErrorException {
+    StringBuilder whereParamSb = new StringBuilder();
+    whereParamSb.append("{")
+      // in
+      .append("age: _.in([0, 1, 2, 3]),")
+      // 小于
+      .append("due: _.lt('Mar 29, 2020 8:47:07 AM'),")
+      // 存在属性
+      .append("price: _.exists(true)")
+      .append("}");
+
+//    // Hutool创建有序map,返回LinkedHashMap
+//    Map<String, Integer> map2 = MapUtil.newHashMap(true);
+//    map2.put("_id", "asc");
+//    map2.put("price", "desc");
+
+    // 有序map
+    ImmutableSortedMap<String, String> orderBy = new ImmutableSortedMap
+      .Builder<String, String>(Ordering.natural())
+      .put("_id", "asc")
+      .put("price", "desc")
+      .build();
+
+    final WxCloudDatabaseQueryResult result = this.wxMaService.getCloudService().query(COLLECTION,
+      whereParamSb.toString(), orderBy, 20, 20);
+    assertThat(result).isNotNull();
+    assertThat(result.getPager()).isNotNull();
+    assertThat(result.getPager().getLimit()).isEqualTo(10);
+    assertThat(result.getPager().getOffset()).isEqualTo(1);
+    assertThat(result.getPager().getTotal()).isGreaterThan(0);
+    assertThat(result.getData().length).isGreaterThan(0);
+  }
+
+  @Test
   public void testDatabaseQuery() throws WxErrorException {
-    final WxCloudDatabaseQueryResult result = this.wxMaService.getCloudService().databaseQuery("rcn",
+    final WxCloudDatabaseQueryResult result = this.wxMaService.getCloudService().databaseQuery(
       "db.collection(\"geo\").where({done:false}).limit(10).skip(1).get()");
     assertThat(result).isNotNull();
     assertThat(result.getPager()).isNotNull();
@@ -94,21 +248,36 @@ public class WxMaCloudServiceImplTest {
 
   @Test
   public void testDatabaseAggregate() throws WxErrorException {
-    final JsonArray result = this.wxMaService.getCloudService().databaseAggregate("rcn",
+    final JsonArray result = this.wxMaService.getCloudService().databaseAggregate(
       "db.collection(\"geo\").aggregate().match({tags:\"cloud\"}).limit(10).end()");
     assertThat(result).isNotNull();
   }
 
   @Test
+  public void testCount() throws WxErrorException {
+    StringBuilder whereParamSb = new StringBuilder();
+    whereParamSb.append("{")
+      // in
+      .append("age: _.in([0, 1, 2, 3]),")
+      // 小于
+      .append("due: _.lt('Mar 29, 2020 8:47:07 AM'),")
+      // 存在属性
+      .append("price: _.exists(true)")
+      .append("}");
+    final Long result = this.wxMaService.getCloudService().count(COLLECTION, whereParamSb.toString());
+    assertThat(result).isGreaterThan(0);
+  }
+
+  @Test
   public void testDatabaseCount() throws WxErrorException {
-    final Long result = this.wxMaService.getCloudService().databaseCount("rcn",
+    final Long result = this.wxMaService.getCloudService().databaseCount(
       "db.collection(\"geo\").where({done:false}).count()");
     assertThat(result).isGreaterThan(0);
   }
 
   @Test
   public void testUpdateIndex() throws WxErrorException {
-    this.wxMaService.getCloudService().updateIndex("rcn", "geo",
+    this.wxMaService.getCloudService().updateIndex(COLLECTION,
       Lists.newArrayList(new WxCloudDatabaseCreateIndexRequest()
         .setName("drop_index")
         .setUnique(true)
@@ -119,15 +288,16 @@ public class WxMaCloudServiceImplTest {
 
   @Test
   public void testDatabaseMigrateImport() throws WxErrorException {
-    final Long result = this.wxMaService.getCloudService().databaseMigrateImport("rcn", "geo", "test.json",
+    final Long result = this.wxMaService.getCloudService().databaseMigrateImport(COLLECTION, "test.json",
       1, true, 1);
     assertThat(result).isGreaterThan(0);
   }
 
   @Test
   public void testDatabaseMigrateExport() throws WxErrorException {
-    final Long result = this.wxMaService.getCloudService().databaseMigrateExport("rcn", "test.json", 1,
-      "const Point = db.Geo.Point;db.collection('geo').where({age: _.gt(1)}).limit(10).skip(1).orderBy('age','asc').orderBy('name', 'desc')" +
+    final Long result = this.wxMaService.getCloudService().databaseMigrateExport("test.json", 1,
+      "const Point = db.Geo.Point;db.collection('geo').where({age: _.gt(1)}).limit(10).skip(1).orderBy('age','asc')" +
+        ".orderBy('name', 'desc')" +
         ".field({ name: true }).get()");
     assertThat(result).isGreaterThan(0);
   }
@@ -135,14 +305,15 @@ public class WxMaCloudServiceImplTest {
   @Test
   public void testDatabaseMigrateQueryInfo() throws WxErrorException {
     final WxCloudCloudDatabaseMigrateQueryInfoResult result = this.wxMaService.getCloudService()
-      .databaseMigrateQueryInfo("rcn", 476629L);
+      .databaseMigrateQueryInfo(476629L);
     assertThat(result).isNotNull();
     System.out.println(result.getFileUrl());
   }
 
   @Test
   public void testUploadFile() throws WxErrorException {
-    final WxCloudUploadFileResult result = this.wxMaService.getCloudService().uploadFile("rcn", "E:\\MyDocs\\Desktop\\test.json");
+    final WxCloudUploadFileResult result = this.wxMaService.getCloudService().uploadFile("E:\\MyDocs\\Desktop" +
+      "\\test.json");
 
     assertThat(result).isNotNull();
     assertThat(result.getAuthorization()).isNotNull();
@@ -155,8 +326,9 @@ public class WxMaCloudServiceImplTest {
 
   @Test
   public void testBatchDownloadFile() throws WxErrorException {
-    final WxCloudBatchDownloadFileResult result = this.wxMaService.getCloudService().batchDownloadFile("rcn",
-      new String[]{"cloud://rcn.7263-rcn-1258788140/Snipaste_2019-11-13_00-21-27.png", "cloud://rcn.7263-rcn-1258788140/avatar.jpg"},
+    final WxCloudBatchDownloadFileResult result = this.wxMaService.getCloudService().batchDownloadFile(
+      new String[]{"cloud://rcn.7263-rcn-1258788140/Snipaste_2019-11-13_00-21-27.png", "cloud://rcn" +
+        ".7263-rcn-1258788140/avatar.jpg"},
       new long[]{7200, 6800});
 
     assertThat(result).isNotNull();
@@ -171,7 +343,7 @@ public class WxMaCloudServiceImplTest {
 
   @Test
   public void testBatchDeleteFile() throws WxErrorException {
-    final WxCloudBatchDeleteFileResult result = this.wxMaService.getCloudService().batchDeleteFile("rcn",
+    final WxCloudBatchDeleteFileResult result = this.wxMaService.getCloudService().batchDeleteFile(
       new String[]{"cloud://rcn.7263-rcn-1258788140/test.json"});
 
     assertThat(result).isNotNull();
@@ -195,18 +367,19 @@ public class WxMaCloudServiceImplTest {
 
   @Test
   public void testDatabaseCollectionAdd() throws WxErrorException {
-    this.wxMaService.getCloudService().databaseCollectionAdd("rcn","test_add_collection");
+    this.wxMaService.getCloudService().databaseCollectionAdd("test_add_collection");
   }
 
   @Test
   public void testDatabaseCollectionDelete() throws WxErrorException {
-    this.wxMaService.getCloudService().databaseCollectionAdd("rcn","test_delete_collection");
-    this.wxMaService.getCloudService().databaseCollectionDelete("rcn","test_delete_collection");
+    this.wxMaService.getCloudService().databaseCollectionAdd("test_delete_collection");
+    this.wxMaService.getCloudService().databaseCollectionDelete("test_delete_collection");
   }
 
   @Test
   public void testDatabaseCollectionGet() throws WxErrorException {
-    final WxCloudDatabaseCollectionGetResult result = this.wxMaService.getCloudService().databaseCollectionGet("rcn", null, null);
+    final WxCloudDatabaseCollectionGetResult result = this.wxMaService.getCloudService().databaseCollectionGet(
+      null, null);
 
     assertThat(result).isNotNull();
     assertThat(result.getPager()).isNotNull();

+ 1 - 0
weixin-java-miniapp/src/test/resources/test-config-sample.xml

@@ -4,6 +4,7 @@
   <secret>secret</secret>
   <token>Token</token>
   <aesKey>EncodingAESKey</aesKey>
+  <cloudEnv>云环境</cloudEnv>
   <accessToken>可以不填写</accessToken>
   <expiresTime>可以不填写</expiresTime>
   <openid>某个用户的openId</openid>

BIN
weixin-java-miniapp/src/test/resources/wx-ma-jssecacerts


BIN
weixin-java-miniapp/src/test/resources/wx-mp-jssecacerts


+ 30 - 3
weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenInMemoryConfigStorage.java

@@ -46,7 +46,6 @@ public class WxOpenInMemoryConfigStorage implements WxOpenConfigStorage {
   private Map<String, Token> cardApiTickets = new ConcurrentHashMap<>();
 
 
-
   @Override
   public boolean isComponentAccessTokenExpired() {
     return System.currentTimeMillis() > componentExpiresTime;
@@ -79,7 +78,8 @@ public class WxOpenInMemoryConfigStorage implements WxOpenConfigStorage {
   }
 
   @Override
-  public void setWxOpenInfo(String componentAppId, String componentAppSecret, String componentToken, String componentAesKey) {
+  public void setWxOpenInfo(String componentAppId, String componentAppSecret, String componentToken,
+                            String componentAesKey) {
     setComponentAppId(componentAppId);
     setComponentAppSecret(componentAppSecret);
     setComponentToken(componentToken);
@@ -146,7 +146,8 @@ public class WxOpenInMemoryConfigStorage implements WxOpenConfigStorage {
 
   @Override
   public void updateAuthorizerAccessToken(String appId, WxOpenAuthorizerAccessToken authorizerAccessToken) {
-    updateAuthorizerAccessToken(appId, authorizerAccessToken.getAuthorizerAccessToken(), authorizerAccessToken.getExpiresIn());
+    updateAuthorizerAccessToken(appId, authorizerAccessToken.getAuthorizerAccessToken(),
+      authorizerAccessToken.getExpiresIn());
   }
 
   @Override
@@ -202,6 +203,14 @@ public class WxOpenInMemoryConfigStorage implements WxOpenConfigStorage {
   private static class WxOpenInnerConfigStorage implements WxMpConfigStorage, WxMaConfig {
     private WxOpenConfigStorage wxOpenConfigStorage;
     private String appId;
+    /**
+     * 小程序原始ID
+     */
+    private volatile String originalId;
+    /**
+     * 云环境ID
+     */
+    private volatile String cloudEnv;
     private Lock accessTokenLock = new ReentrantLock();
     private Lock jsapiTicketLock = new ReentrantLock();
     private Lock cardApiTicketLock = new ReentrantLock();
@@ -326,6 +335,24 @@ public class WxOpenInMemoryConfigStorage implements WxOpenConfigStorage {
     }
 
     @Override
+    public String getOriginalId() {
+      return originalId;
+    }
+
+    public void setOriginalId(String originalId) {
+      this.originalId = originalId;
+    }
+
+    @Override
+    public String getCloudEnv() {
+      return this.cloudEnv;
+    }
+
+    public void setCloudEnv(String cloudEnv) {
+      this.cloudEnv = cloudEnv;
+    }
+
+    @Override
     public void expireAccessToken() {
       wxOpenConfigStorage.expireAuthorizerAccessToken(appId);
     }