oa.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. package work
  2. import (
  3. "encoding/json"
  4. "encoding/xml"
  5. "fmt"
  6. "github.com/gogf/gf/util/gconv"
  7. "github.com/silenceper/wechat/v2/credential"
  8. "github.com/silenceper/wechat/v2/util"
  9. "github.com/silenceper/wechat/v2/work/config"
  10. "github.com/silenceper/wechat/v2/work/context"
  11. "github.com/silenceper/wechat/v2/work/kf"
  12. "strconv"
  13. "time"
  14. )
  15. const (
  16. getTemplateDetailURL = "https://qyapi.weixin.qq.com/cgi-bin/oa/gettemplatedetail?access_token=%s"
  17. applyEventURL = "https://qyapi.weixin.qq.com/cgi-bin/oa/applyevent?access_token=%s"
  18. getApprovalInfoURL = "https://qyapi.weixin.qq.com/cgi-bin/oa/getapprovalinfo?access_token=%s"
  19. getApprovalDetailURL = "https://qyapi.weixin.qq.com/cgi-bin/oa/getapprovaldetail?access_token=%s"
  20. )
  21. // Work 企业微信
  22. type Work struct {
  23. ctx *context.Context
  24. }
  25. // NewWork init work
  26. func NewWork(cfg *config.Config) *Work {
  27. defaultAkHandle := credential.NewWorkAccessToken(cfg.CorpID, cfg.CorpSecret, credential.CacheKeyWorkPrefix, cfg.Cache)
  28. ctx := &context.Context{
  29. Config: cfg,
  30. AccessTokenHandle: defaultAkHandle,
  31. }
  32. return &Work{ctx: ctx}
  33. }
  34. // GetOATemplateDetail 获取审批模板详情
  35. func (work *Work) GetOATemplateDetail(templateID string) (*OATemplateDetail, error) {
  36. accessToken, err := work.ctx.GetAccessToken()
  37. if err != nil {
  38. return nil, err
  39. }
  40. response, err := util.PostJSON(fmt.Sprintf(getTemplateDetailURL, accessToken), reqOAGetTemplateDetail{
  41. TemplateID: templateID,
  42. })
  43. if err != nil {
  44. return nil, err
  45. }
  46. var result respOAGetTemplateDetail
  47. err = json.Unmarshal(response, &result)
  48. if err != nil {
  49. return nil, err
  50. }
  51. if result.ErrCode != 0 {
  52. return nil, kf.NewSDKErr(result.ErrCode, result.ErrMsg)
  53. }
  54. return &result.OATemplateDetail, nil
  55. }
  56. // ApplyOAEvent 提交审批申请
  57. func (work *Work) ApplyOAEvent(applyInfo OAApplyEvent) (string, error) {
  58. accessToken, err := work.ctx.GetAccessToken()
  59. if err != nil {
  60. return "", err
  61. }
  62. fmt.Println("-------------企业微信审批流对接【提交审批申请】请求---------------", gconv.String(applyInfo))
  63. response, err := util.PostJSON(fmt.Sprintf(applyEventURL, accessToken), applyInfo)
  64. if err != nil {
  65. return "", err
  66. }
  67. var result respOAApplyEvent
  68. err = json.Unmarshal(response, &result)
  69. if err != nil {
  70. return "", err
  71. }
  72. fmt.Println("-------------企业微信审批流对接【提交审批申请】返回---------------", gconv.String(result))
  73. if result.ErrCode != 0 {
  74. return "", kf.NewSDKErr(result.ErrCode, result.ErrMsg)
  75. }
  76. return result.SpNo, nil
  77. }
  78. // GetOAApprovalInfo 批量获取审批单号
  79. func (work *Work) GetOAApprovalInfo(req GetOAApprovalInfoReq) ([]string, error) {
  80. accessToken, err := work.ctx.GetAccessToken()
  81. if err != nil {
  82. return nil, err
  83. }
  84. response, err := util.PostJSON(fmt.Sprintf(getApprovalInfoURL, accessToken), reqOAGetApprovalInfo{
  85. StartTime: strconv.FormatInt(req.StartTime.Unix(), 10),
  86. EndTime: strconv.FormatInt(req.EndTime.Unix(), 10),
  87. Cursor: req.Cursor,
  88. Size: req.Size,
  89. Filters: req.Filters,
  90. })
  91. if err != nil {
  92. return nil, err
  93. }
  94. var result respOAGetApprovalInfo
  95. err = json.Unmarshal(response, &result)
  96. if err != nil {
  97. return nil, err
  98. }
  99. if result.ErrCode != 0 {
  100. return nil, kf.NewSDKErr(result.ErrCode, result.ErrMsg)
  101. }
  102. return result.SpNoList, nil
  103. }
  104. // GetOAApprovalDetail 获取审批申请详情
  105. func (work *Work) GetOAApprovalDetail(spNo string) (*OAApprovalDetail, error) {
  106. accessToken, err := work.ctx.GetAccessToken()
  107. if err != nil {
  108. return nil, err
  109. }
  110. fmt.Println("-------------企业微信审批流对接【获取审批申请详情】请求---------------", spNo)
  111. response, err := util.PostJSON(fmt.Sprintf(getApprovalDetailURL, accessToken), reqOAGetApprovalDetail{
  112. SpNo: spNo,
  113. })
  114. if err != nil {
  115. return nil, err
  116. }
  117. var result respOAGetApprovalDetail
  118. err = json.Unmarshal(response, &result)
  119. if err != nil {
  120. return nil, err
  121. }
  122. fmt.Println("-------------企业微信审批流对接【获取审批申请详情】请求---------------", gconv.String(result))
  123. if result.ErrCode != 0 {
  124. return nil, kf.NewSDKErr(result.ErrCode, result.ErrMsg)
  125. }
  126. return &result.Info, nil
  127. }
  128. // GetOAApprovalInfoReq 批量获取审批单号请求
  129. type GetOAApprovalInfoReq struct {
  130. // StartTime 审批单提交的时间范围,开始时间,UNix时间戳
  131. StartTime time.Time
  132. // EndTime 审批单提交的时间范围,结束时间,Unix时间戳
  133. EndTime time.Time
  134. // Cursor 分页查询游标,默认为0,后续使用返回的next_cursor进行分页拉取
  135. Cursor int
  136. // Size 一次请求拉取审批单数量,默认值为100,上限值为100
  137. Size uint32
  138. // Filters 筛选条件,可对批量拉取的审批申请设置约束条件,支持设置多个条件
  139. Filters []OAApprovalInfoFilter
  140. }
  141. // OAApprovalInfo 审批申请状态变化回调通知
  142. type OAApprovalInfo struct {
  143. // SpNo 审批编号
  144. SpNo string `xml:"SpNo"`
  145. // SpName 审批申请类型名称(审批模板名称)
  146. SpName string `xml:"SpName"`
  147. // SpStatus 申请单状态:1-审批中;2-已通过;3-已驳回;4-已撤销;6-通过后撤销;7-已删除;10-已支付
  148. SpStatus string `xml:"SpStatus"`
  149. // TemplateID 审批模板id。可在“获取审批申请详情”、“审批状态变化回调通知”中获得,也可在审批模板的模板编辑页面链接中获得。
  150. TemplateID string `xml:"TemplateId"`
  151. // ApplyTime 审批申请提交时间,Unix时间戳
  152. ApplyTime string `xml:"ApplyTime"`
  153. // Applicant 申请人信息
  154. Applicant OAApprovalInfoApplicant `xml:"Applyer"`
  155. // SpRecord 审批流程信息,可能有多个审批节点。
  156. SpRecord []OAApprovalInfoSpRecord `xml:"SpRecord"`
  157. // Notifier 抄送信息,可能有多个抄送节点
  158. Notifier OAApprovalInfoNotifier `xml:"Notifyer"`
  159. // Comments 审批申请备注信息,可能有多个备注节点
  160. Comments []OAApprovalInfoComment `xml:"Comments"`
  161. // StatusChangeEvent 审批申请状态变化类型:1-提单;2-同意;3-驳回;4-转审;5-催办;6-撤销;8-通过后撤销;10-添加备注
  162. StatusChangeEvent string `xml:"StatuChangeEvent"`
  163. }
  164. // OAApprovalInfoApplicant 申请人信息
  165. type OAApprovalInfoApplicant struct {
  166. // UserID 申请人userid
  167. UserID string `xml:"UserId"`
  168. // Party 申请人所在部门pid
  169. Party string `xml:"Party"`
  170. }
  171. // OAApprovalInfoSpRecord 审批流程信息,可能有多个审批节点。
  172. type OAApprovalInfoSpRecord struct {
  173. // SpStatus 审批节点状态:1-审批中;2-已同意;3-已驳回;4-已转审
  174. SpStatus string `xml:"SpStatus"`
  175. // ApproverAttr 节点审批方式:1-或签;2-会签
  176. ApproverAttr string `xml:"ApproverAttr"`
  177. // Details 审批节点详情。当节点为标签或上级时,一个节点可能有多个分支
  178. Details []OAApprovalInfoSpRecordDetail `xml:"Details"`
  179. }
  180. // OAApprovalInfoSpRecordDetail 审批节点详情。当节点为标签或上级时,一个节点可能有多个分支
  181. type OAApprovalInfoSpRecordDetail struct {
  182. // Approver 分支审批人
  183. Approver OAApprovalInfoSpRecordDetailApprover `xml:"Approver"`
  184. // Speech 审批意见字段
  185. Speech string `xml:"Speech"`
  186. // SpStatus 分支审批人审批状态:1-审批中;2-已同意;3-已驳回;4-已转审
  187. SpStatus string `xml:"SpStatus"`
  188. // SpTime 节点分支审批人审批操作时间,0为尚未操作
  189. SpTime string `xml:"SpTime"`
  190. // Attach 节点分支审批人审批意见附件,赋值为media_id具体使用请参考:文档-获取临时素材
  191. Attach []string `xml:"Attach"`
  192. }
  193. // OAApprovalInfoSpRecordDetailApprover 分支审批人
  194. type OAApprovalInfoSpRecordDetailApprover struct {
  195. // UserID 分支审批人userid
  196. UserID string `xml:"UserId"`
  197. }
  198. // OAApprovalInfoNotifier 抄送信息,可能有多个抄送节点
  199. type OAApprovalInfoNotifier struct {
  200. // UserID 节点抄送人userid
  201. UserID string `xml:"UserId"`
  202. }
  203. // OAApprovalInfoComment 审批申请备注信息,可能有多个备注节点
  204. type OAApprovalInfoComment struct {
  205. // CommentUserInfo 备注人信息
  206. CommentUserInfo OAApprovalInfoCommentUserInfo `xml:"CommentUserInfo"`
  207. // CommentTime 备注提交时间
  208. CommentTime string `xml:"CommentTime"`
  209. // CommentContent 备注文本内容
  210. CommentContent string `xml:"CommentContent"`
  211. // CommentID 备注id
  212. CommentID string `xml:"CommentId"`
  213. // Attach 备注意见附件,值是附件media_id具体使用请参考:文档-获取临时素材
  214. Attach []string `xml:"Attach"`
  215. }
  216. // OAApprovalInfoCommentUserInfo 备注人信息
  217. type OAApprovalInfoCommentUserInfo struct {
  218. // UserID 备注人userid
  219. UserID string `xml:"UserId"`
  220. }
  221. type SysApprovalChangeMessage struct {
  222. XMLName xml.Name `xml:"xml"`
  223. Text string `xml:",chardata"`
  224. ToUserName struct {
  225. Text string `xml:",chardata"`
  226. } `xml:"ToUserName"`
  227. FromUserName struct {
  228. Text string `xml:",chardata"`
  229. } `xml:"FromUserName"`
  230. CreateTime struct {
  231. Text string `xml:",chardata"`
  232. } `xml:"CreateTime"`
  233. MsgType struct {
  234. Text string `xml:",chardata"`
  235. } `xml:"MsgType"`
  236. Event struct {
  237. Text string `xml:",chardata"`
  238. } `xml:"Event"`
  239. AgentID struct {
  240. Text string `xml:",chardata"`
  241. } `xml:"AgentID"`
  242. ApprovalInfo struct {
  243. Text string `xml:",chardata"`
  244. SpNo struct {
  245. Text string `xml:",chardata"`
  246. } `xml:"SpNo"`
  247. SpName struct {
  248. Text string `xml:",chardata"`
  249. } `xml:"SpName"`
  250. SpStatus struct {
  251. Text string `xml:",chardata"`
  252. } `xml:"SpStatus"`
  253. TemplateId struct {
  254. Text string `xml:",chardata"`
  255. } `xml:"TemplateId"`
  256. ApplyTime struct {
  257. Text string `xml:",chardata"`
  258. } `xml:"ApplyTime"`
  259. Applyer struct {
  260. Text string `xml:",chardata"`
  261. UserId struct {
  262. Text string `xml:",chardata"`
  263. } `xml:"UserId"`
  264. Party struct {
  265. Text string `xml:",chardata"`
  266. } `xml:"Party"`
  267. } `xml:"Applyer"`
  268. SpRecord []struct {
  269. Text string `xml:",chardata"`
  270. SpStatus struct {
  271. Text string `xml:",chardata"`
  272. } `xml:"SpStatus"`
  273. ApproverAttr struct {
  274. Text string `xml:",chardata"`
  275. } `xml:"ApproverAttr"`
  276. Details []struct {
  277. Text string `xml:",chardata"`
  278. Approver struct {
  279. Text string `xml:",chardata"`
  280. UserId struct {
  281. Text string `xml:",chardata"`
  282. } `xml:"UserId"`
  283. } `xml:"Approver"`
  284. Speech struct {
  285. Text string `xml:",chardata"`
  286. } `xml:"Speech"`
  287. SpStatus struct {
  288. Text string `xml:",chardata"`
  289. } `xml:"SpStatus"`
  290. SpTime struct {
  291. Text string `xml:",chardata"`
  292. } `xml:"SpTime"`
  293. } `xml:"Details"`
  294. } `xml:"SpRecord"`
  295. Notifyer struct {
  296. Text string `xml:",chardata"`
  297. UserId struct {
  298. Text string `xml:",chardata"`
  299. } `xml:"UserId"`
  300. } `xml:"Notifyer"`
  301. Comments struct {
  302. Text string `xml:",chardata"`
  303. CommentUserInfo struct {
  304. Text string `xml:",chardata"`
  305. UserId struct {
  306. Text string `xml:",chardata"`
  307. } `xml:"UserId"`
  308. } `xml:"CommentUserInfo"`
  309. CommentTime struct {
  310. Text string `xml:",chardata"`
  311. } `xml:"CommentTime"`
  312. CommentContent struct {
  313. Text string `xml:",chardata"`
  314. } `xml:"CommentContent"`
  315. CommentId struct {
  316. Text string `xml:",chardata"`
  317. } `xml:"CommentId"`
  318. } `xml:"Comments"`
  319. StatuChangeEvent struct {
  320. Text string `xml:",chardata"`
  321. } `xml:"StatuChangeEvent"`
  322. } `xml:"ApprovalInfo"`
  323. }