Selaa lähdekoodia

:new: #1641 企业微信增加OA提交审批申请的接口

Binary Wang 4 vuotta sitten
vanhempi
commit
f6f4b89fc0

+ 16 - 0
weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxCpErrorMsgEnum.java

@@ -1070,10 +1070,26 @@ public enum WxCpErrorMsgEnum {
    */
   CODE_301024(301024, "获取打卡记录时间间隔超限;保证开始时间大于0 且结束时间大于 0 且结束时间大于开始时间,且间隔少于93天"),
   /**
+   * 提交审批单请求参数错误
+   */
+  CODE_301025(301025,"提交审批单请求参数错误"),
+  /**
    * 不允许更新该用户的userid.
    */
   CODE_301036(301036, "不允许更新该用户的userid"),
   /**
+   * 无审批应用权限,或者提单者不在审批应用/自建应用的可见范围
+   */
+  CODE_301055(301055,"无审批应用权限,或者提单者不在审批应用/自建应用的可见范围"),
+  /**
+   * 审批应用已停用
+   */
+  CODE_301056(301056,"审批应用已停用"),
+  /**
+   * 通用错误码,提交审批单内部接口失败
+   */
+  CODE_301057(301057,"通用错误码,提交审批单内部接口失败"),
+  /**
    * 批量导入任务的文件中userid有重复.
    */
   CODE_302003(302003, "批量导入任务的文件中userid有重复"),

+ 27 - 10
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpOaService.java

@@ -16,6 +16,22 @@ import java.util.List;
 public interface WxCpOaService {
 
   /**
+   * <pre>提交审批申请
+   * 调试工具
+   * 企业可通过审批应用或自建应用Secret调用本接口,代应用可见范围内员工在企业微信“审批应用”内提交指定类型的审批申请。
+   *
+   * 请求方式:POST(HTTPS)
+   * 请求地址: https://qyapi.weixin.qq.com/cgi-bin/oa/applyevent?access_token=ACCESS_TOKEN
+   * 文档地址:https://work.weixin.qq.com/api/doc/90000/90135/91853
+   * </pre>
+   *
+   * @param request 请求
+   * @return 表单提交成功后,返回的表单编号
+   * @throws WxErrorException .
+   */
+  String apply(WxCpOaApplyEventRequest request) throws WxErrorException;
+
+  /**
    * <pre>
    *  获取打卡数据
    *  API doc : https://work.weixin.qq.com/api/doc#90000/90135/90262
@@ -40,7 +56,7 @@ public interface WxCpOaService {
    * @param datetime   需要获取规则的当天日期
    * @param userIdList 需要获取打卡规则的用户列表
    * @return 打卡规则列表
-   * @throws WxErrorException
+   * @throws WxErrorException .
    */
   List<WxCpCheckinOption> getCheckinOption(Date datetime, List<String> userIdList) throws WxErrorException;
 
@@ -63,7 +79,7 @@ public interface WxCpOaService {
    * @param size      一次请求拉取审批单数量,默认值为100,上限值为100
    * @param filters   筛选条件,可对批量拉取的审批申请设置约束条件,支持设置多个条件,nullable
    * @return WxCpApprovalInfo
-   * @throws WxErrorException
+   * @throws WxErrorException .
    */
   WxCpApprovalInfo getApprovalInfo(@NonNull Date startTime, @NonNull Date endTime, Integer cursor, Integer size,
                                    List<WxCpApprovalInfoQueryFilter> filters) throws WxErrorException;
@@ -74,7 +90,7 @@ public interface WxCpOaService {
    * @param startTime 开始时间
    * @param endTime   结束时间
    * @return WxCpApprovalInfo
-   * @throws WxErrorException
+   * @throws WxErrorException .
    * @see me.chanjar.weixin.cp.api.WxCpOaService#getApprovalInfo
    */
   WxCpApprovalInfo getApprovalInfo(@NonNull Date startTime, @NonNull Date endTime) throws WxErrorException;
@@ -90,7 +106,7 @@ public interface WxCpOaService {
    *
    * @param spNo 审批单编号。
    * @return WxCpApprovaldetail
-   * @throws WxErrorException
+   * @throws WxErrorException .
    */
   WxCpApprovalDetailResult getApprovalDetail(@NonNull String spNo) throws WxErrorException;
 
@@ -104,7 +120,7 @@ public interface WxCpOaService {
    * @param startTime 获取审批记录的开始时间
    * @param endTime   获取审批记录的结束时间
    * @param nextSpnum 第一个拉取的审批单号,不填从该时间段的第一个审批单拉取
-   * @throws WxErrorException
+   * @throws WxErrorException .
    * @see me.chanjar.weixin.cp.api.WxCpOaService#getApprovalInfo
    * @see me.chanjar.weixin.cp.api.WxCpOaService#getApprovalDetail
    */
@@ -118,18 +134,19 @@ public interface WxCpOaService {
    * @param endTime   查询的结束时间戳
    * @param offset    分页查询的偏移量
    * @param limit     分页查询的每页大小,默认为100条,如该参数大于100则按100处理
-   * @return
-   * @throws WxErrorException
+   * @return .
+   * @throws WxErrorException .
    */
   List<WxCpDialRecord> getDialRecord(Date startTime, Date endTime, Integer offset,
                                      Integer limit) throws WxErrorException;
 
   /**
    * 获取审批模板详情
+   *
    * @param templateId 模板ID
-   * @return
-   * @throws WxErrorException
+   * @return .
+   * @throws WxErrorException .
    */
-  WxCpTemplateResult getTemplateDetail(@NonNull String templateId)throws WxErrorException;
+  WxCpTemplateResult getTemplateDetail(@NonNull String templateId) throws WxErrorException;
 
 }

+ 14 - 7
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpOaServiceImpl.java

@@ -32,6 +32,13 @@ public class WxCpOaServiceImpl implements WxCpOaService {
   private static final int USER_IDS_LIMIT = 100;
 
   @Override
+  public String apply(WxCpOaApplyEventRequest request) throws WxErrorException {
+    String responseContent = this.mainService.post(this.mainService.getWxCpConfigStorage().getApiUrl(APPLY_EVENT),
+      request.toJson());
+    return GsonParser.parse(responseContent).get("sp_no").getAsString();
+  }
+
+  @Override
   public List<WxCpCheckinData> getCheckinData(Integer openCheckinDataType, Date startTime, Date endTime,
                                               List<String> userIdList) throws WxErrorException {
     if (startTime == null || endTime == null) {
@@ -42,10 +49,10 @@ public class WxCpOaServiceImpl implements WxCpOaService {
       throw new RuntimeException("用户列表不能为空,不超过 " + USER_IDS_LIMIT + " 个,若用户超过 " + USER_IDS_LIMIT + " 个,请分批获取");
     }
 
-    long endtimestamp = endTime.getTime() / 1000L;
-    long starttimestamp = startTime.getTime() / 1000L;
+    long endTimestamp = endTime.getTime() / 1000L;
+    long startTimestamp = startTime.getTime() / 1000L;
 
-    if (endtimestamp - starttimestamp < 0 || endtimestamp - starttimestamp >= MONTH_SECONDS) {
+    if (endTimestamp - startTimestamp < 0 || endTimestamp - startTimestamp >= MONTH_SECONDS) {
       throw new RuntimeException("获取记录时间跨度不超过一个月");
     }
 
@@ -53,8 +60,8 @@ public class WxCpOaServiceImpl implements WxCpOaService {
     JsonArray jsonArray = new JsonArray();
 
     jsonObject.addProperty("opencheckindatatype", openCheckinDataType);
-    jsonObject.addProperty("starttime", starttimestamp);
-    jsonObject.addProperty("endtime", endtimestamp);
+    jsonObject.addProperty("starttime", startTimestamp);
+    jsonObject.addProperty("endtime", endTimestamp);
 
     for (String userid : userIdList) {
       jsonArray.add(userid);
@@ -213,9 +220,9 @@ public class WxCpOaServiceImpl implements WxCpOaService {
   @Override
   public WxCpTemplateResult getTemplateDetail(@NonNull String templateId) throws WxErrorException {
     JsonObject jsonObject = new JsonObject();
-    jsonObject.addProperty("template_id",templateId);
+    jsonObject.addProperty("template_id", templateId);
     final String url = this.mainService.getWxCpConfigStorage().getApiUrl(GET_TEMPLATE_DETAIL);
     String responseContent = this.mainService.post(url, jsonObject.toString());
-    return WxCpGsonBuilder.create().fromJson(responseContent,WxCpTemplateResult.class);
+    return WxCpGsonBuilder.create().fromJson(responseContent, WxCpTemplateResult.class);
   }
 }

+ 41 - 0
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/SummaryInfo.java

@@ -0,0 +1,41 @@
+package me.chanjar.weixin.cp.bean.oa;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import lombok.experimental.Accessors;
+import me.chanjar.weixin.cp.bean.oa.WxCpOaApplyEventRequest;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 摘要行信息,用于定义某一行摘要显示的内容.
+ *
+ * @author <a href="https://github.com/binarywang">Binary Wang</a>
+ * @date 2020-07-19
+ */
+@Data
+@Accessors(chain = true)
+public class SummaryInfo implements Serializable {
+  private static final long serialVersionUID = 8262265774851382414L;
+  /**
+   * 摘要行信息,用于定义某一行摘要显示的内容
+   */
+  @SerializedName("summary_info")
+  private List<SummaryInfoData> summaryInfoData;
+
+  @Data
+  @Accessors(chain = true)
+  public static class SummaryInfoData implements Serializable {
+    private static final long serialVersionUID = 5314161929610113856L;
+    /**
+     * 摘要行显示文字,用于记录列表和消息通知的显示,不要超过20个字符
+     */
+    private String text;
+
+    /**
+     * 摘要行显示语言
+     */
+    private String lang;
+  }
+}

+ 1 - 1
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalApplyer.java

@@ -10,7 +10,7 @@ import java.io.Serializable;
  * @author element
  */
 @Data
-public class WxCpApprovalApplyer extends WxCpOperator implements Serializable {
+public class WxCpApprovalApplier extends WxCpOperator implements Serializable {
 
   private static final long serialVersionUID = -8974662568286821271L;
 

+ 2 - 2
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalApplyData.java

@@ -1,7 +1,7 @@
 package me.chanjar.weixin.cp.bean.oa;
 
 import lombok.Data;
-import me.chanjar.weixin.cp.bean.oa.applydata.Content;
+import me.chanjar.weixin.cp.bean.oa.applydata.ApplyDataContent;
 
 import java.io.Serializable;
 import java.util.List;
@@ -16,6 +16,6 @@ public class WxCpApprovalApplyData implements Serializable {
 
   private static final long serialVersionUID = 4061352949894274704L;
 
-  private List<Content> contents;
+  private List<ApplyDataContent> contents;
 
 }

+ 1 - 1
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpApprovalDetail.java

@@ -49,7 +49,7 @@ public class WxCpApprovalDetail implements Serializable {
    * 申请人信息
    */
   @SerializedName("applyer")
-  private WxCpApprovalApplyer applier;
+  private WxCpApprovalApplier applier;
 
   /**
    * 审批流程信息,可能有多个审批节点

+ 105 - 0
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpOaApplyEventRequest.java

@@ -0,0 +1,105 @@
+package me.chanjar.weixin.cp.bean.oa;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import lombok.experimental.Accessors;
+import me.chanjar.weixin.cp.bean.oa.applydata.ApplyDataContent;
+import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 提交审批申请 请求对象类.
+ *
+ * @author <a href="https://github.com/binarywang">Binary Wang</a>
+ * @date 2020-07-18
+ */
+@Data
+@Accessors(chain = true)
+public class WxCpOaApplyEventRequest implements Serializable {
+  private static final long serialVersionUID = 3362660678938569341L;
+
+  /**
+   * 申请人userid,此审批申请将以此员工身份提交,申请人需在应用可见范围内
+   */
+  @SerializedName("creator_userid")
+  private String creatorUserId;
+
+  /**
+   * 模板id。可在“获取审批申请详情”、“审批状态变化回调通知”中获得,也可在审批模板的模板编辑页面链接中获得。暂不支持通过接口提交[打卡补卡][调班]模板审批单。
+   */
+  @SerializedName("template_id")
+  private String templateId;
+
+  /**
+   * 审批人模式:0-通过接口指定审批人、抄送人(此时approver、notifyer等参数可用); 1-使用此模板在管理后台设置的审批流程,支持条件审批。默认为0
+   */
+  @SerializedName("use_template_approver")
+  private Integer useTemplateApprover;
+
+  /**
+   * 审批流程信息,用于指定审批申请的审批流程,支持单人审批、多人会签、多人或签,可能有多个审批节点,仅use_template_approver为0时生效。
+   */
+  @SerializedName("approver")
+  private List<Approver> approvers;
+
+  /**
+   * 抄送人节点userid列表,仅use_template_approver为0时生效。
+   */
+  @SerializedName("notifyer")
+  private String[] notifiers;
+
+  /**
+   * 抄送方式:1-提单时抄送(默认值); 2-单据通过后抄送;3-提单和单据通过后抄送。仅use_template_approver为0时生效。
+   */
+  @SerializedName("notify_type")
+  private Integer notifyType;
+
+  /**
+   * 审批申请数据,可定义审批申请中各个控件的值,其中必填项必须有值,选填项可为空,数据结构同“获取审批申请详情”接口返回值中同名参数“apply_data”
+   */
+  @SerializedName("apply_data")
+  private ApplyData applyData;
+
+  /**
+   * 摘要信息,用于显示在审批通知卡片、审批列表的摘要信息,最多3行
+   */
+  @SerializedName("summary_list")
+  private List<SummaryInfo> summaryList;
+
+  public String toJson() {
+    return WxCpGsonBuilder.create().toJson(this);
+  }
+
+  @Data
+  @Accessors(chain = true)
+  public static class Approver implements Serializable {
+    private static final long serialVersionUID = 7625206971546930988L;
+
+    /**
+     * 节点审批方式:1-或签;2-会签,仅在节点为多人审批时有效
+     */
+    private Integer attr;
+
+    /**
+     * 审批节点审批人userid列表,若为多人会签、多人或签,需填写每个人的userid
+     */
+    @SerializedName("userid")
+    private String[] userIds;
+  }
+
+  @Data
+  @Accessors(chain = true)
+  public static class ApplyData implements Serializable {
+    private static final long serialVersionUID = -2462732405265306981L;
+
+    /**
+     * 审批申请数据,可定义审批申请中各个控件的值,其中必填项必须有值,选填项可为空,
+     * 数据结构同“获取审批申请详情”接口返回值中同名参数“apply_data”
+     */
+    @SerializedName("contents")
+    private List<ApplyDataContent> contents;
+  }
+
+}

+ 35 - 0
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/applydata/ApplyDataContent.java

@@ -0,0 +1,35 @@
+package me.chanjar.weixin.cp.bean.oa.applydata;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * @author element
+ */
+@Data
+@Accessors(chain = true)
+public class ApplyDataContent implements Serializable {
+  private static final long serialVersionUID = 8456821731930526935L;
+  /**
+   * 控件类型:Text-文本;Textarea-多行文本;Number-数字;Money-金额;Date-日期/日期+时间;
+   * Selector-单选/多选;;Contact-成员/部门;Tips-说明文字;File-附件;Table-明细;
+   */
+  private String control;
+
+  /**
+   * 控件id:控件的唯一id,可通过“获取审批模板详情”接口获取
+   */
+  private String id;
+
+  @SerializedName("title")
+  private List<ContentTitle> titles;
+
+  /**
+   * 控件值 ,需在此为申请人在各个控件中填写内容不同控件有不同的赋值参数,具体说明详见附录。模板配置的控件属性为必填时,对应value值需要有值。
+   */
+  private ContentValue value;
+}

+ 0 - 24
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/applydata/Content.java

@@ -1,24 +0,0 @@
-package me.chanjar.weixin.cp.bean.oa.applydata;
-
-import com.google.gson.annotations.SerializedName;
-import lombok.Data;
-
-import java.io.Serializable;
-import java.util.List;
-
-/**
- * @author element
- */
-@Data
-public class Content implements Serializable {
-  private static final long serialVersionUID = 8456821731930526935L;
-
-  private String control;
-
-  private String id;
-
-  @SerializedName("title")
-  private List<ContentTitle> titles;
-
-  private ContentValue value;
-}

+ 3 - 2
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/applydata/ContentValue.java

@@ -2,6 +2,7 @@ package me.chanjar.weixin.cp.bean.oa.applydata;
 
 import com.google.gson.annotations.SerializedName;
 import lombok.Data;
+import lombok.experimental.Accessors;
 
 import java.io.Serializable;
 import java.util.List;
@@ -10,6 +11,7 @@ import java.util.List;
  * @author element
  */
 @Data
+@Accessors(chain = true)
 public class ContentValue implements Serializable {
   private static final long serialVersionUID = -5607678965965065261L;
 
@@ -90,7 +92,7 @@ public class ContentValue implements Serializable {
   @Data
   public static class Child implements Serializable {
     private static final long serialVersionUID = -3500102073821161558L;
-    private List<Content> list;
+    private List<ApplyDataContent> list;
   }
 
 
@@ -115,5 +117,4 @@ public class ContentValue implements Serializable {
   }
 
 
-
 }

+ 15 - 3
weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpOaServiceImplTest.java

@@ -81,12 +81,24 @@ public class WxCpOaServiceImplTest {
   }
 
   @Test
-  public void testGetTemplateDetail() throws WxErrorException{
-    String templateId="3TkZjxugodbqpEMk9j7X6h6zKqYkc7MxQrrFmT7H";
-    WxCpTemplateResult result=wxService.getOAService().getTemplateDetail(templateId);
+  public void testGetTemplateDetail() throws WxErrorException {
+    String templateId = "3TkZjxugodbqpEMk9j7X6h6zKqYkc7MxQrrFmT7H";
+    WxCpTemplateResult result = wxService.getOAService().getTemplateDetail(templateId);
     assertThat(result).isNotNull();
     System.out.println("result ");
     System.out.println(gson.toJson(result));
   }
 
+  @Test
+  public void testApply() throws WxErrorException {
+    this.wxService.getOAService().apply(new WxCpOaApplyEventRequest().setCreatorUserId("123"));
+  }
+
+  @Test
+  public void testGetApprovalData() {
+  }
+
+  @Test
+  public void testGetDialRecord() {
+  }
 }

+ 92 - 0
weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/oa/WxCpOaApplyEventRequestTest.java

@@ -0,0 +1,92 @@
+package me.chanjar.weixin.cp.bean.oa;
+
+import me.chanjar.weixin.common.util.json.GsonParser;
+import me.chanjar.weixin.cp.bean.oa.applydata.ApplyDataContent;
+import me.chanjar.weixin.cp.bean.oa.applydata.ContentValue;
+import org.testng.annotations.Test;
+
+import java.util.Arrays;
+import java.util.Collections;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * 测试.
+ *
+ * @author <a href="https://github.com/binarywang">Binary Wang</a>
+ * @date 2020-07-18
+ */
+public class WxCpOaApplyEventRequestTest {
+  @Test
+  public void testToJson() {
+    String json = "{\n" +
+      "    \"creator_userid\": \"WangXiaoMing\",\n" +
+      "    \"template_id\": \"3Tka1eD6v6JfzhDMqPd3aMkFdxqtJMc2ZRioeFXkaaa\",\n" +
+      "    \"use_template_approver\":0,\n" +
+      "    \"approver\": [\n" +
+      "        {\n" +
+      "            \"attr\": 2,\n" +
+      "            \"userid\": [\"WuJunJie\",\"WangXiaoMing\"]\n" +
+      "        },\n" +
+      "        {\n" +
+      "            \"attr\": 1,\n" +
+      "            \"userid\": [\"LiuXiaoGang\"]\n" +
+      "        }\n" +
+      "    ],\n" +
+      "    \"notifyer\":[ \"WuJunJie\",\"WangXiaoMing\" ],\n" +
+      "    \"notify_type\" : 1,\n" +
+      "    \"apply_data\": {\n" +
+      "         \"contents\": [\n" +
+      "                {\n" +
+      "                    \"control\": \"Text\",\n" +
+      "                    \"id\": \"Text-15111111111\",\n" +
+      "                    \"value\": {\n" +
+      "                        \"text\": \"文本填写的内容\"\n" +
+      "                    }\n" +
+      "                }\n" +
+      "            ]\n" +
+      "    },\n" +
+      "    \"summary_list\": [\n" +
+      "        {\n" +
+      "            \"summary_info\": [{\n" +
+      "                \"text\": \"摘要第1行\",\n" +
+      "                \"lang\": \"zh_CN\"\n" +
+      "            }]\n" +
+      "        },\n" +
+      "        {\n" +
+      "            \"summary_info\": [{\n" +
+      "                \"text\": \"摘要第2行\",\n" +
+      "                \"lang\": \"zh_CN\"\n" +
+      "            }]\n" +
+      "        },\n" +
+      "        {\n" +
+      "            \"summary_info\": [{\n" +
+      "                \"text\": \"摘要第3行\",\n" +
+      "                \"lang\": \"zh_CN\"\n" +
+      "            }]\n" +
+      "        }\n" +
+      "    ]\n" +
+      "}";
+
+    WxCpOaApplyEventRequest request = new WxCpOaApplyEventRequest();
+    request.setCreatorUserId("WangXiaoMing")
+      .setTemplateId("3Tka1eD6v6JfzhDMqPd3aMkFdxqtJMc2ZRioeFXkaaa")
+      .setUseTemplateApprover(0)
+      .setApprovers(Arrays.asList(new WxCpOaApplyEventRequest.Approver().setAttr(2).setUserIds(new String[]{"WuJunJie", "WangXiaoMing"}),
+        new WxCpOaApplyEventRequest.Approver().setAttr(1).setUserIds(new String[]{"LiuXiaoGang"})))
+      .setNotifiers(new String[]{"WuJunJie", "WangXiaoMing"})
+      .setNotifyType(1)
+      .setApplyData(new WxCpOaApplyEventRequest.ApplyData()
+        .setContents(Collections.singletonList(new ApplyDataContent()
+          .setControl("Text").setId("Text-15111111111").setValue(new ContentValue().setText("文本填写的内容")))))
+      .setSummaryList(Arrays.asList(new SummaryInfo()
+          .setSummaryInfoData(Collections.singletonList(new SummaryInfo.SummaryInfoData().setLang("zh_CN").setText("摘要第1行"))),
+        new SummaryInfo()
+          .setSummaryInfoData(Collections.singletonList(new SummaryInfo.SummaryInfoData().setLang("zh_CN").setText("摘要第2行"))),
+        new SummaryInfo()
+          .setSummaryInfoData(Collections.singletonList(new SummaryInfo.SummaryInfoData().setLang("zh_CN").setText("摘要第3行")))))
+    ;
+
+    assertThat(request.toJson()).isEqualTo(GsonParser.parse(json).toString());
+  }
+}