work_flow.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. package workflow
  2. import (
  3. "context"
  4. "dashoo.cn/micro/app/dao/work"
  5. dao "dashoo.cn/micro/app/dao/workflow"
  6. model "dashoo.cn/micro/app/model/workflow"
  7. "dashoo.cn/micro/app/service"
  8. "dashoo.cn/opms_libary/myerrors"
  9. "dashoo.cn/opms_libary/plugin/dingtalk"
  10. "dashoo.cn/opms_libary/plugin/dingtalk/message"
  11. "dashoo.cn/opms_libary/plugin/dingtalk/workflow"
  12. WXwork "dashoo.cn/opms_libary/plugin/wechat/work"
  13. "dashoo.cn/opms_libary/utils"
  14. "fmt"
  15. "github.com/gogf/gf/frame/g"
  16. "github.com/gogf/gf/os/gtime"
  17. "github.com/gogf/gf/util/gconv"
  18. "github.com/silenceper/wechat/v2/cache"
  19. "github.com/silenceper/wechat/v2/work/config"
  20. "time"
  21. )
  22. type workflowService struct {
  23. *service.ContextService
  24. Dao *dao.PlatWorkflowDao
  25. wfDao *work.WorkOrderTypeDao
  26. }
  27. func NewFlowService(ctx context.Context) (svc *workflowService, err error) {
  28. svc = new(workflowService)
  29. if svc.ContextService, err = svc.Init(ctx); err != nil {
  30. return nil, err
  31. }
  32. svc.Dao = dao.NewPlatWorkflowDao(svc.Tenant)
  33. svc.wfDao = work.NewWorkOrderTypeDao(svc.Tenant)
  34. return svc, nil
  35. }
  36. // GetSchemaByProcessCode 获取表单 schema(钉钉接口)
  37. func (s *workflowService) GetSchemaByProcessCode(processCode string) (*workflow.QuerySchemaByProcessCodeResponse, error) {
  38. g.Log().Info("搜索值", processCode)
  39. // 调用钉钉接口
  40. client := dingtalk.NewClient()
  41. w := client.GetWorkflow()
  42. resp, err := w.QuerySchemaByProcessCode(processCode)
  43. if err != nil {
  44. return nil, err
  45. }
  46. return &resp, nil
  47. }
  48. // GetProcessInstanceDetail 获取审批实例详情(钉钉接口)
  49. func (s *workflowService) GetProcessInstanceDetail(instId string) (*workflow.QueryProcessInstanceResponse, error) {
  50. g.Log().Info("搜索值", instId)
  51. // 调用钉钉接口
  52. client := dingtalk.NewClient()
  53. w := client.GetWorkflow()
  54. resp, err := w.QueryProcessInstanceDetail(instId)
  55. if err != nil {
  56. return nil, err
  57. }
  58. return &resp, nil
  59. }
  60. // DiffOrgApprovalStructConversion 不同企业审批流请求结构体转换
  61. func (s *workflowService) DiffOrgApprovalStructConversion(bizType string, approvalData g.Map) (string, interface{}, error) {
  62. ent, err := s.wfDao.Where(s.wfDao.C.BusCode, model.BusCodeConst[bizType]).One()
  63. if err != nil {
  64. return "", nil, err
  65. }
  66. fmt.Println(ent)
  67. formItemList := gconv.Maps(ent.FormColumn)
  68. switch ent.WorkflowOrg {
  69. case "10": // 钉钉审批
  70. appInfo := &workflow.StartProcessInstanceRequest{
  71. ProcessCode: &ent.WorkflowCode,
  72. FormComponentValues: []*workflow.StartProcessInstanceRequestFormComponentValues{},
  73. }
  74. applyData := make([]*workflow.StartProcessInstanceRequestFormComponentValues, 0)
  75. for _, item := range formItemList {
  76. itemMap := gconv.MapStrStr(item)
  77. switch itemMap["component"] {
  78. case "TableField":
  79. rowList := make([][]*workflow.StartProcessInstanceRequestFormComponentValues, 0)
  80. for _, product := range gconv.Maps(approvalData[itemMap["code"]]) {
  81. colList := make([]*workflow.StartProcessInstanceRequestFormComponentValues, 0)
  82. for _, valMap := range gconv.Maps(itemMap["children"]) {
  83. valStrMap := gconv.MapStrStr(valMap)
  84. val := workflow.StartProcessInstanceRequestFormComponentValues{
  85. Id: utils.String(valStrMap["id"]),
  86. Name: utils.String(valStrMap["label"]),
  87. Value: utils.String(gconv.String(product[valStrMap["code"]])),
  88. }
  89. colList = append(colList, &val)
  90. }
  91. rowList = append(rowList, colList)
  92. }
  93. formValues := workflow.StartProcessInstanceRequestFormComponentValues{
  94. Id: utils.String(itemMap["id"]),
  95. Name: utils.String(itemMap["label"]),
  96. Value: utils.String(gconv.String(rowList)),
  97. }
  98. applyData = append(applyData, &formValues)
  99. default:
  100. formValues := workflow.StartProcessInstanceRequestFormComponentValues{
  101. Id: utils.String(itemMap["id"]),
  102. Name: utils.String(itemMap["label"]),
  103. Value: utils.String(gconv.String(approvalData[itemMap["code"]])),
  104. }
  105. applyData = append(applyData, &formValues)
  106. }
  107. }
  108. appInfo.FormComponentValues = applyData
  109. return ent.WorkflowOrg, appInfo, nil
  110. case "20": // 企业微信审批
  111. appInfo := WXwork.OAApplyEvent{
  112. CreatorUserID: s.CxtUser.WechatId,
  113. TemplateID: ent.WorkflowCode,
  114. UseTemplateApprover: 1,
  115. ApplyData: WXwork.OAContents{
  116. Contents: []WXwork.OAContent{},
  117. },
  118. SummaryList: []WXwork.OASummaryList{{SummaryInfo: []WXwork.OAText{{
  119. Text: gconv.String(approvalData["abstract"]),
  120. Lang: "zh_CN",
  121. }}}},
  122. }
  123. applyData := make([]WXwork.OAContent, 0)
  124. for _, item := range formItemList {
  125. itemMap := gconv.MapStrStr(item)
  126. content, err := WeChatStructConversion(itemMap, approvalData)
  127. if err != nil {
  128. return "", nil, err
  129. }
  130. applyData = append(applyData, content)
  131. }
  132. appInfo.ApplyData.Contents = applyData
  133. return ent.WorkflowOrg, appInfo, err
  134. default:
  135. return "", nil, myerrors.TipsError("不支持的审批流组织")
  136. }
  137. }
  138. func WeChatStructConversion(itemMap g.MapStrStr, approvalData g.Map) (WXwork.OAContent, error) {
  139. content := WXwork.OAContent{
  140. ID: itemMap["id"],
  141. Title: []WXwork.OAText{WXwork.OAText{
  142. Text: itemMap["label"],
  143. Lang: "zh_CN",
  144. }},
  145. }
  146. switch itemMap["component"] {
  147. case string(WXwork.OAControlText):
  148. content.Control = WXwork.OAControlText
  149. content.Value = WXwork.OAContentValue{
  150. Text: gconv.String(approvalData[itemMap["code"]]),
  151. }
  152. case string(WXwork.OAControlTextarea):
  153. content.Control = WXwork.OAControlTextarea
  154. content.Value = WXwork.OAContentValue{
  155. Text: gconv.String(approvalData[itemMap["code"]]),
  156. }
  157. case string(WXwork.OAControlNumber):
  158. content.Control = WXwork.OAControlNumber
  159. content.Value = WXwork.OAContentValue{
  160. Number: gconv.String(approvalData[itemMap["code"]]),
  161. }
  162. case string(WXwork.OAControlMoney):
  163. content.Control = WXwork.OAControlMoney
  164. content.Value = WXwork.OAContentValue{
  165. Money: gconv.String(approvalData[itemMap["code"]]),
  166. }
  167. case string(WXwork.OAControlFormula):
  168. content.Control = WXwork.OAControlFormula
  169. content.Value = WXwork.OAContentValue{
  170. Formula: WXwork.OAContentFormula{Value: gconv.String(approvalData[itemMap["code"]])},
  171. }
  172. case string(WXwork.OAControlDate):
  173. content.Control = WXwork.OAControlDate
  174. content.Value = WXwork.OAContentValue{
  175. Date: WXwork.OAContentDate{Type: "day", Timestamp: gconv.String(gconv.GTime(approvalData[itemMap["code"]]).Timestamp())},
  176. }
  177. case string(WXwork.OAControlDateRange):
  178. content.Control = WXwork.OAControlDateRange
  179. content.Value = WXwork.OAContentValue{
  180. DateRange: WXwork.OAContentDateRange{
  181. NewBegin: int(gconv.GTime(approvalData[itemMap["code"]]).Timestamp()),
  182. NewEnd: int(gconv.GTime(approvalData[itemMap["code"]]).Timestamp()),
  183. },
  184. }
  185. case string(WXwork.OAControlSelector) + "-single":
  186. content.Control = WXwork.OAControlSelector
  187. content.Value = WXwork.OAContentValue{
  188. Selector: WXwork.OAContentSelector{
  189. Type: "single",
  190. Options: []WXwork.OAContentSelectorOption{
  191. {Key: gconv.String(approvalData[itemMap["code"]])},
  192. },
  193. },
  194. }
  195. case string(WXwork.OAControlSelector) + "-multi":
  196. content.Control = WXwork.OAControlSelector
  197. content.Value = WXwork.OAContentValue{
  198. Selector: WXwork.OAContentSelector{
  199. Type: "multi",
  200. Options: []WXwork.OAContentSelectorOption{},
  201. },
  202. }
  203. vals := gconv.Strings(approvalData[itemMap["code"]])
  204. for _, val := range vals {
  205. content.Value.Selector.Options = append(content.Value.Selector.Options, WXwork.OAContentSelectorOption{Key: val})
  206. }
  207. case string(WXwork.OAControlContact):
  208. content.Control = WXwork.OAControlContact
  209. content.Value = WXwork.OAContentValue{
  210. Members: []WXwork.OAContentMember{{
  211. UserID: "your_userid",
  212. Name: "your_name",
  213. }},
  214. }
  215. case string(WXwork.OAControlLocation):
  216. content.Control = WXwork.OAControlLocation
  217. content.Value = WXwork.OAContentValue{
  218. Location: WXwork.OAContentLocation{
  219. Latitude: "30.547239",
  220. Longitude: "104.063291",
  221. Title: "腾讯科技(成都)有限公司(腾讯成都大厦)",
  222. Address: "四川省成都市武侯区天府三街198号腾讯成都大厦A座",
  223. Time: int(time.Now().Unix()),
  224. },
  225. }
  226. case string(WXwork.OAControlRelatedApproval):
  227. content.Control = WXwork.OAControlRelatedApproval
  228. content.Value = WXwork.OAContentValue{
  229. RelatedApproval: []WXwork.OAContentRelatedApproval{
  230. {SpNo: gconv.String(approvalData[itemMap["code"]])},
  231. },
  232. }
  233. case string(WXwork.OAControlTable):
  234. content.Control = WXwork.OAControlTable
  235. dataList := gconv.Maps(approvalData[itemMap["code"]])
  236. rowList := make([]WXwork.OAContentTableList, 0)
  237. for _, product := range dataList {
  238. colList := make([]WXwork.OAContent, 0)
  239. for _, valMap := range gconv.Maps(itemMap["children"]) {
  240. valStrMap := gconv.MapStrStr(valMap)
  241. val, err := WeChatStructConversion(valStrMap, product)
  242. if err != nil {
  243. return content, err
  244. }
  245. colList = append(colList, val)
  246. }
  247. rowList = append(rowList, WXwork.OAContentTableList{List: colList})
  248. }
  249. content.Value = WXwork.OAContentValue{
  250. Table: rowList,
  251. }
  252. default:
  253. return content, myerrors.TipsError("不支持的组件类型。")
  254. }
  255. return content, nil
  256. }
  257. // CheckStartProcessInstance 判断发起审批流实例
  258. func (s *workflowService) CheckStartProcessInstance(bizCode, bizType, remark string, approvalData g.Map) (instanceId string, err error) {
  259. workflowOrg, approvalReq, err := s.DiffOrgApprovalStructConversion(bizType, approvalData)
  260. if err != nil {
  261. return "", err
  262. }
  263. switch workflowOrg {
  264. case "10": // 钉钉审批
  265. return s.StartProcessInstance(bizCode, bizType, remark, approvalReq.(*workflow.StartProcessInstanceRequest))
  266. case "20": // 企业微信审批
  267. return s.WXStartProcessInstance(bizCode, bizType, remark, approvalReq.(WXwork.OAApplyEvent))
  268. }
  269. return "", myerrors.TipsError("审批类型错误")
  270. }
  271. // WXStartProcessInstance 企业微信发起审批
  272. func (s *workflowService) WXStartProcessInstance(bizCode, bizType, remark string, approvalReq WXwork.OAApplyEvent) (instanceId string, err error) {
  273. conf := &config.Config{
  274. CorpID: g.Config().GetString("wechat.corp_id"),
  275. CorpSecret: g.Config().GetString("wechat.corp_secret"),
  276. AgentID: g.Config().GetString("wechat.agent_id"),
  277. Token: g.Config().GetString("wechat.token"),
  278. EncodingAESKey: g.Config().GetString("wechat.encoding_aes_key"),
  279. Cache: cache.NewMemory(),
  280. }
  281. client := WXwork.NewWork(conf)
  282. spNo, err := client.ApplyOAEvent(approvalReq)
  283. if err != nil {
  284. return "", err
  285. }
  286. _, err = s.saveWorkflowInstance(bizCode, bizType, remark, spNo)
  287. return spNo, err
  288. }
  289. // StartProcessInstance 发起一个新的审批流(钉钉接口,并回填到本地系统)
  290. // OriginatorUserId = utils.String("当前用户的钉钉中的Id")
  291. // ProcessCode = utils.String("每种审批对应固定的code")
  292. // bizType:业务类型(10领用20项目创建30合同创建
  293. // 详情参照文档:https://open.dingtalk.com/document/orgapp/create-an-approval-instance
  294. func (s *workflowService) StartProcessInstance(bizCode, bizType, remark string, flow *workflow.StartProcessInstanceRequest) (instanceId string, err error) {
  295. // 参数调整
  296. if flow.OriginatorUserId == nil {
  297. flow.OriginatorUserId = utils.String(s.GetCxtUserDingtalkUid())
  298. }
  299. if flow.DeptId == nil {
  300. flow.DeptId = utils.Int64(gconv.Int64(s.GetCxtUserDeptId()))
  301. }
  302. g.Log().Info("搜索值", flow)
  303. // 调用钉钉接口
  304. client := dingtalk.NewClient()
  305. w := client.GetWorkflow()
  306. resp, err := w.StartProcessInstance(flow)
  307. if err != nil {
  308. return "", err
  309. }
  310. _, err = s.saveWorkflowInstance(bizCode, bizType, remark, resp.InstanceId)
  311. return resp.InstanceId, err
  312. }
  313. // RevokeProcessInstance 撤销审批实例(钉钉接口)
  314. func (s *workflowService) RevokeProcessInstance(instId, remark string) (string, error) {
  315. g.Log().Info("搜索值instId, remark:", instId, remark)
  316. // 调用钉钉接口
  317. client := dingtalk.NewClient()
  318. w := client.GetWorkflow()
  319. resp, err := w.RevokeProcessInstance(instId, remark, s.GetCxtUserDingtalkUid())
  320. if err != nil {
  321. return "", err
  322. }
  323. return resp.InstanceId, nil
  324. }
  325. // 将审批实例同步到数据库中
  326. func (s *workflowService) saveWorkflowInstance(bizCode, bizType, remark, instanceId string) (insertId int64, err error) {
  327. now := gtime.Now()
  328. // 构造基础数据
  329. var instance model.PlatWorkflow
  330. instance.BizCode = bizCode
  331. instance.BizType = bizType
  332. instance.CurrentNode = "create"
  333. instance.CurrentNodeTime = now.Format("Y-m-d H:i:s")
  334. instance.ProcessInstId = instanceId
  335. instance.Remark = remark
  336. // 填充创建信息
  337. service.SetCreatedInfo(&instance, s.GetCxtUserId(), s.GetCxtUserName())
  338. // 保存数据并返回
  339. return s.Dao.InsertAndGetId(instance)
  340. }
  341. // GetWorkflowInstance 获取实例(本地数据)
  342. func (s *workflowService) GetWorkflowInstance(instanceId string) (*model.PlatWorkflow, error) {
  343. // 查询日程实例
  344. return s.Dao.Where("process_inst_id", instanceId).FindOne()
  345. }
  346. // Update 更新(本地数据)
  347. func (s *workflowService) Update(instance *model.PlatWorkflow, msg *message.MixMessage) (err error) {
  348. // 保存实例
  349. instance.CurrentNode = msg.ProcessType
  350. instance.CurrentNodeTime = time.Unix(gconv.Int64(msg.FinishTime), 0).Format("2006-01-02 15:04:05")
  351. instance.ApprovalResult = msg.Result
  352. _, err = s.Dao.Save(instance)
  353. return
  354. }
  355. // GetList 任务信息列表(本地数据)
  356. func (s *workflowService) GetList(req *model.SearchWorkflowReq) (total int, flowList []*model.PlatWorkflow, err error) {
  357. flowModel := s.Dao.M
  358. if req.BizCode != "" {
  359. flowModel = flowModel.Where("biz_code", req.BizCode)
  360. }
  361. if req.BizType != "" {
  362. flowModel = flowModel.Where("biz_type", req.BizType)
  363. }
  364. if req.MySelf != "0" {
  365. flowModel = flowModel.Where("created_by", s.GetCxtUserId())
  366. }
  367. total, err = flowModel.Count()
  368. if err != nil {
  369. g.Log().Error(err)
  370. err = myerrors.DbError("获取总行数失败。")
  371. return
  372. }
  373. err = flowModel.Page(req.GetPage()).Order("plat_task.created_time DESC").Scan(&flowList)
  374. return
  375. }