踩坑,一肚子火,记录下过程!
001.获取审批使用的Secret(并非企业使用的CorpSecret)
002.设置接收事件服务器并勾选需要订阅的应用
003.订阅事件接入(golang代码)
v1.GET("/oa_event", verifyurl.Check) //审批接入
package verifyurl
import (
"net/http"
"wx_chat/src/global"
"wx_chat/src/utils/bind"
"github.com/gin-gonic/gin"
)
type P_VerifyUrl struct {
Signature string `form:"msg_signature" binding:"required"`
Timestamp string `form:"timestamp" binding:"required"`
Nonce string `form:"nonce" binding:"required"`
Echostr string `form:"echostr" binding:"required"`
}
func Check(c *gin.Context) {
var p P_VerifyUrl
if bind.Bind(c, &p, true) == nil {
if echostr, err := global.Wxcpt.VerifyURL(p.Signature, p.Timestamp, p.Nonce, p.Echostr); err != nil {
c.String(http.StatusForbidden, err.ErrMsg)
} else {
c.String(http.StatusOK, string(echostr))
}
}
}
004.解析消息(golang)
v1.POST("/oa_event", wxoa.Events) //消息订阅
package wxoa
import (
"encoding/xml"
"errors"
"fmt"
"io/ioutil"
"wx_chat/src/global"
"wx_chat/src/response"
"wx_chat/src/utils/bind"
"github.com/gin-gonic/gin"
)
type P_Events struct {
Signature string `form:"msg_signature" binding:"required"`
Timestamp string `form:"timestamp" binding:"required"`
Nonce string `form:"nonce" binding:"required"`
}
type D_Events struct {
ToUserName string `xml:"ToUserName"`
CreateTime string `xml:"CreateTime"`
MsgType string `xml:"MsgType"`
Event string `xml:"Event"` //事件名称:sys_approval_change
AgentID string `xml:"AgentID"`
ApprovalInfo ApprovalInfo `xml:"ApprovalInfo"`
}
type ApprovalInfo struct {
SpNo string `xml:"SpNo"` //审批编号
SpName string `xml:"SpName"` //审批申请类型名称(审批模板名称)
SpStatus string `xml:"SpStatus"` //申请单状态:1-审批中;2-已通过;3-已驳回;4-已撤销;6-通过后撤销;7-已删除;10-已支付
TemplateId string `xml:"TemplateId"` //审批模板id。可在“获取审批申请详情”、“审批状态变化回调通知”中获得,也可在审批模板的模板编辑页面链接中获得。
ApplyTime string `xml:"ApplyTime"` //审批申请提交时间,Unix时间戳
Applyer ApplyerInfo `xml:"Applyer"` //申请人信息
SpRecord []SpRecordInfo `xml:"SpRecord"` //审批流程信息,可能有多个审批节点。
Notifyer []NotifyerInfo `xml:"Notifyer"` //抄送信息,可能有多个抄送节点
Comments []CommentsInfo `xml:"Comments"` //审批申请备注信息,可能有多个备注节点
}
type ApplyerInfo struct {
UserId string `xml:"UserId"` //申请人userid
Party string `xml:"Party"` //申请人所在部门pid
}
type SpRecordInfo struct {
SpStatus string `xml:"SpStatus"` //审批节点状态:1-审批中;2-已同意;3-已驳回;4-已转审
ApproverAttr string `xml:"ApproverAttr"` //节点审批方式:1-或签;2-会签
Details SpRecordDetails `xml:"Details"` //审批节点详情。当节点为标签或上级时,一个节点可能有多个分支
}
type SpRecordDetails struct {
Approver SpRecordApprover `xml:"Approver"` //分支审批人
Speech string `xml:"Speech"` //审批意见字段
SpStatus string `xml:"SpStatus"` //分支审批人审批状态:1-审批中;2-已同意;3-已驳回;4-已转审
SpTime string `xml:"SpTime"` //节点分支审批人审批操作时间,0为尚未操作
Attach string `xml:"Attach"` //节点分支审批人审批意见附件,赋值为media_id具体使用请参考:文档-获取临时素材
}
type SpRecordApprover struct {
UserId string `xml:"UserId"` //分支审批人userid
}
type NotifyerInfo struct {
UserId string `xml:"UserId"` //节点抄送人userid
}
type CommentsInfo struct {
UserId string `xml:"UserId"` //备注人userid
}
func Events(c *gin.Context) {
var p P_Events
if bind.Bind(c, &p, true) == nil {
var res D_Events
if buf, err := ioutil.ReadAll(c.Request.Body); err != nil {
response.ServerError(c, err)
return
} else if msg, err := global.Wxcpt.DecryptMsg(p.Signature, p.Timestamp, p.Nonce, buf); err != nil {
response.ServerError(c, errors.New(err.ErrMsg))
return
} else if err := xml.Unmarshal(msg, &res); err != nil {
response.ServerError(c, err)
return
} else if res.MsgType == "event" && res.Event == "sys_approval_change" {
//处理消息
fmt.Println(res)
}
}
}
005.提交审批(golang)
package wxoa
import (
"fmt"
"wx_chat/src/modules/accesstoken"
"wx_chat/src/utils/http"
)
type P_ApplyOrder struct {
CreatorUserid string `json:"creator_userid"` //申请人userid,此审批申请将以此员工身份提交,申请人需在应用可见范围内
TemplateId string `json:"template_id"` //模板id。可在“获取审批申请详情”、“审批状态变化回调通知”中获得,也可在审批模板的模板编辑页面链接中获得。暂不支持通过接口提交[打卡补卡][调班]模板审批单。
UseTemplateApprover int `json:"use_template_approver"` //审批人模式:0-通过接口指定审批人、抄送人(此时approver、notifyer等参数可用); 1-使用此模板在管理后台设置的审批流程(需要保证审批流程中没有“申请人自选”节点),支持条件审批。默认为0
ChooseDepartment int `json:"choose_department"` //提单者提单部门id,不填默认为主部门
//Approver []ApproverInfo `json:"approver"` //审批流程信息,用于指定审批申请的审批流程,支持单人审批、多人会签、多人或签,可能有多个审批节点,仅use_template_approver为0时生效。
//Notifyer []string `json:"notifyer"` //抄送人节点userid列表,仅use_template_approver为0时生效。
NotifyType int `json:"notify_type"` //抄送方式:1-提单时抄送(默认值); 2-单据通过后抄送;3-提单和单据通过后抄送。仅use_template_approver为0时生效。
ApplyData ApplyDataInfo `json:"apply_data,omitempty"` //审批申请数据,可定义审批申请中各个控件的值,其中必填项必须有值,选填项可为空,数据结构同“获取审批申请详情”接口返回值中同名参数“apply_data”
}
type ApproverInfo struct {
Userid []string `json:"userid"` //审批节点审批人userid列表,若为多人会签、多人或签,需填写每个人的userid
Attr int `json:"attr"` //节点审批方式:1-或签;2-会签,仅在节点为多人审批时有效
}
type ApplyDataInfo struct {
Contents []ContentsInfo `json:"contents"` //审批申请详情,由多个表单控件及其内容组成,其中包含需要对控件赋值的信息
}
type ContentsInfo struct {
Control string `json:"control"` //控件类型:Text-文本;Textarea-多行文本;Number-数字;Money-金额;Date-日期/日期+时间;Selector-单选/多选;;Contact-成员/部门;Tips-说明文字;File-附件;Table-明细;Location-位置;RelatedApproval-关联审批单;Formula-公式;DateRange-时长;
Id string `json:"id"` //控件id:控件的唯一id,可通过“获取审批模板详情”接口获取
Value ValueInfo `json:"value"` //控件值 ,需在此为申请人在各个控件中填写内容不同控件有不同的赋值参数,具体说明详见附录。模板配置的控件属性为必填时,对应value值需要有值。
}
type ValueInfo struct {
Text string `json:"text"` //
//"text": "文本填写的内容"
//"new_number": "700"
//"new_money": "700"
//"date": {"type": "day","s_timestamp": "1569859200"}
//"selector": {"type": "multi","options": [{"key": "option-15111111111"},{"key": "option-15222222222"}]}
//"members": [{"userid": "WuJunJie","name": "Jackie"},{"userid": "WangXiaoMing","name": "Tom"}]
//"departments": [{"openapi_id": "2","name": "销售部"},{"openapi_id": "3","name": "生产部"}]
//"files": [{"file_id": "1G6nrLmr5EC3MMb_-zK1dDdzmd0p7cNliYu9V5w7o8K1aaa"}]
//"children": [控件]
}
func ApplyOrder() (err error) {
//获取token
var token string
if token, err = accesstoken.GetOA(); err != nil {
return
}
//请求消息
url := fmt.Sprintf("https://qyapi.weixin.qq.com/cgi-bin/oa/applyevent?access_token=%s", token)
var body []byte
if body, err = http.Post(url, P_ApplyOrder{
CreatorUserid: "CreatorUserid",
TemplateId: "TemplateId",
UseTemplateApprover: 1, //后台自带审批
ChooseDepartment: 2, //微信商城部门ID
//Notifyer: []string{},
NotifyType: 1,
//Approver: []ApproverInfo{},
ApplyData: ApplyDataInfo{
Contents: []ContentsInfo{
{
Control: "Text",
Id: "Text-1681803392383",
Value: ValueInfo{
Text: "我是测试的审批啊啊啊啊啊",
},
},
},
},
}); err != nil {
return
}
//Text-1681803392383
fmt.Println(string(body))
return
}