diff --git a/api/personalityTest/entry.go b/api/personalityTest/entry.go index 892dcec..4d8aae2 100644 --- a/api/personalityTest/entry.go +++ b/api/personalityTest/entry.go @@ -1,6 +1,6 @@ package personalityTest -import rongxin "appPlatform/api/personalityTest/rongXin" +import rongxin "appPlatform/api/personalityTest/rongXinPage" //性格测试相关 type ApiEntry struct { diff --git a/api/personalityTest/rongXin/controller.go b/api/personalityTest/rongXinPage/controller.go similarity index 99% rename from api/personalityTest/rongXin/controller.go rename to api/personalityTest/rongXinPage/controller.go index 0889ce8..6e93c35 100644 --- a/api/personalityTest/rongXin/controller.go +++ b/api/personalityTest/rongXinPage/controller.go @@ -1,4 +1,4 @@ -package rongxin +package rongXinPage import ( "appPlatform/models/modelshr" diff --git a/api/personalityTest/rongXin/downLoad.go b/api/personalityTest/rongXinPage/downLoad.go similarity index 89% rename from api/personalityTest/rongXin/downLoad.go rename to api/personalityTest/rongXinPage/downLoad.go index 2f8ae93..e6184c6 100644 --- a/api/personalityTest/rongXin/downLoad.go +++ b/api/personalityTest/rongXinPage/downLoad.go @@ -1,4 +1,4 @@ -package rongxin +package rongXinPage import ( "appPlatform/models/modelshr" @@ -318,6 +318,9 @@ func (a *ApiMethod) StatisticsPersonality(c *gin.Context) { adminorg := c.Query("org") typekey := c.Query("typekey") types := c.Query("types") + if typekey != "" { + requestData.Typekey = typekey + } typesInt, _ := strconv.Atoi(types) @@ -349,12 +352,27 @@ func (a *ApiMethod) StatisticsPersonality(c *gin.Context) { builder.WriteString("\xEF\xBB\xBF") //写入UTF-8 BOM 防止乱码 writer := csv.NewWriter(&builder) if typesInt == 2 { + // fmt.Printf("Typekey: %v\n", requestData.Typekey) writer.Write([]string{"已完成测试人员名单"}) + switch requestData.Typekey { + case "10000001": //性格色彩 + writer.Write([]string{"序号", "工号", "姓名", "行政组织", "性格色彩解析"}) + // fmt.Printf("Typekey----1------>: %v\n", requestData.Typekey) + case "10000002": //DISC性格特质 + writer.Write([]string{"序号", "工号", "姓名", "行政组织", "DISC性格解析"}) + // fmt.Printf("Typekey----2------>: %v\n", requestData.Typekey) + case "10000003": //九型人格特质 + writer.Write([]string{"序号", "工号", "姓名", "行政组织", "第一角色", "第二角色", "第三角色", "第四角色"}) + // fmt.Printf("Typekey----3------>: %v\n", requestData.Typekey) + default: + writer.Write([]string{"序号", "工号", "姓名", "行政组织"}) + // fmt.Printf("Typekey----4------>: %v\n", requestData.Typekey) + } } else { writer.Write([]string{"未完成测试人员名单"}) + writer.Write([]string{"序号", "工号", "姓名", "行政组织"}) } - writer.Write([]string{"序号", "工号", "姓名", "行政组织"}) jibuqi := 0 for _, v := range peopleList { if typesInt == 2 { @@ -366,6 +384,16 @@ func (a *ApiMethod) StatisticsPersonality(c *gin.Context) { scvBody = append(scvBody, v.Number) scvBody = append(scvBody, v.Name) scvBody = append(scvBody, jisuanOrg(v.Company, v.MainDeparment, v.AdminOrg)) + switch requestData.Typekey { + case "10000001": //性格色彩 + scvBody = append(scvBody, TallyColor(v)) + case "10000002": //DISC性格特质 + scvBody = append(scvBody, TallDiscCaput(v)) + case "10000003": //九型人格特质 + scvBody = append(scvBody, TallyNineCaput(v)...) + default: + + } writer.Write(scvBody) } } else { @@ -388,6 +416,7 @@ func (a *ApiMethod) StatisticsPersonality(c *gin.Context) { if typesInt == 2 { fileName = fmt.Sprintf("已完成测试人员名单_%v.csv", publicmethod.GetUUid(1)) } + // fmt.Printf("fileName: %v\n", fileName) c.Writer.Header().Add("Content-type", "application/octet-stream") c.Header("Content-Type", "application/vnd.ms-excel;charset=utf8") c.Header("Content-Disposition", "attachment; filename="+url.PathEscape(fileName)) diff --git a/api/personalityTest/rongXinPage/testCaput.go b/api/personalityTest/rongXinPage/testCaput.go new file mode 100644 index 0000000..547ef07 --- /dev/null +++ b/api/personalityTest/rongXinPage/testCaput.go @@ -0,0 +1,296 @@ +package rongXinPage + +import ( + "appPlatform/models/modelshr" + personalitycolor "appPlatform/models/personalityColor" + "appPlatform/overall" + "appPlatform/overall/publicmethod" + "encoding/json" + "fmt" + "sort" + "strconv" + "strings" +) + +/* +* +@ 作者: 秦东 +@ 时间: 2024-12-12 10:07:32 +@ 功能: TallDisc测试 +*/ +func TallDiscCaput(user modelshr.PersonArchives) string { + var cesiJieguo []string + var colorInfo personalitycolor.Charcolortest + colorInfo.GetCont(map[string]interface{}{"`c_states`": 1, "`c_number`": user.Number, "`c_class`": 10000002}, "`c_test_json`") + if colorInfo.TestJson != "" { + var testPage JieShouDaan + json.Unmarshal([]byte(colorInfo.TestJson), &testPage) + d := 0 + i := 0 + s := 0 + cVal := 0 + for _, v := range testPage.List { + switch v.Pick { + case "D": + d++ + case "I": + i++ + case "S": + s++ + case "C": + cVal++ + default: + } + } + // disc := []string{"支配型", "影响型", "稳定型", "服从型"} + discVal := []int{d, i, s, cVal} + maxVal := 0 + for _, mv := range discVal { + if maxVal <= mv { + maxVal = mv + } + } + + if d == maxVal { + cesiJieguo = append(cesiJieguo, "支配型") + } + if i == maxVal { + cesiJieguo = append(cesiJieguo, "影响型") + } + if s == maxVal { + cesiJieguo = append(cesiJieguo, "稳定型") + } + if cVal == maxVal { + cesiJieguo = append(cesiJieguo, "服从型") + } + } + return strings.Join(cesiJieguo, ",") +} + +/* +* +@ 作者: 秦东 +@ 时间: 2024-12-12 09:20:36 +@ 功能: 计算九型人格 +*/ +func TallyNineCaput(user modelshr.PersonArchives) []string { + var xgAry personalitycolor.Charcolortest + overall.CONSTANT_DB_Color.Model(&personalitycolor.Charcolortest{}).Where("`c_states` = 1 AND `c_class` = ? AND `c_number` = ?", 10000003, user.Number).First(&xgAry) + var jieguo []string + if xgAry.TestJson != "" { + var testPage JieShouDaaning + err := json.Unmarshal([]byte(xgAry.TestJson), &testPage) + if err == nil { + scoreForEachItem := CalculateScoresForEachItem(testPage) + sort.Slice(scoreForEachItem, func(i, j int) bool { + return scoreForEachItem[i].Value > scoreForEachItem[j].Value + }) + for i, v := range scoreForEachItem { + if i < 4 { + jieguo = append(jieguo, v.Attribute) + } + } + } + } + return jieguo +} + +/* +* +@ 作者: 秦东 +@ 时间: 2024-12-12 09:48:23 +@ 功能: 计算性格色彩 +*/ +func TallyColor(user modelshr.PersonArchives) string { + var colorMax []string + var myColor personalitycolor.Charcolortest + err := myColor.GetCont(map[string]interface{}{"`c_states`": 1, "`c_class`": 10000001, "`c_number`": user.Number}) + if err != nil { + return strings.Join(colorMax, ",") + } + GetColorVal := make(map[string]int) + if myColor.TestJson != "" { + var testPage []TestPageColor + errJson := json.Unmarshal([]byte(myColor.TestJson), &testPage) + if errJson != nil { + var testPageStr []TestPageColorStr + json.Unmarshal([]byte(myColor.TestJson), &testPageStr) + GetColorVal = JiSuanColorStr(testPageStr) + } else { + GetColorVal = JiSuanColor(testPage) + } + } + + var colorAry []int + colorAry = append(colorAry, GetColorVal["RedColor"]) + colorAry = append(colorAry, GetColorVal["BlueColor"]) + colorAry = append(colorAry, GetColorVal["YellowColor"]) + colorAry = append(colorAry, GetColorVal["GreenColor"]) + + MaxColor := publicmethod.GetMaxNum[int](colorAry) + if GetColorVal["RedColor"] == MaxColor { + colorMax = append(colorMax, "红色") + } + if GetColorVal["BlueColor"] == MaxColor { + colorMax = append(colorMax, "蓝色") + } + if GetColorVal["YellowColor"] == MaxColor { + colorMax = append(colorMax, "黄色") + } + if GetColorVal["GreenColor"] == MaxColor { + colorMax = append(colorMax, "绿色") + } + return strings.Join(colorMax, ",") +} + +// 计算颜色 +func JiSuanColor(testPage []TestPageColor) map[string]int { + if len(testPage) < 1 { + sendDataw := make(map[string]int) + sendDataw["RedColor"] = 0 + sendDataw["BlueColor"] = 0 + sendDataw["YellowColor"] = 0 + sendDataw["GreenColor"] = 0 + return sendDataw + } + A_front_count := 0 + B_front_count := 0 + C_front_count := 0 + D_front_count := 0 + A_after_count := 0 + B_after_count := 0 + C_after_count := 0 + D_after_count := 0 + // fmt.Printf("testPage:%v\n", testPage) + for _, v := range testPage { + // testNumInt, _ := strconv.Atoi(v.TestNumber) + // checkedValInt, _ := strconv.Atoi(v.CheckedVal) + testNumInt := v.TestNumber + checkedValInt := v.CheckedVal + if testNumInt <= 15 { + switch checkedValInt { + case 2: + B_front_count++ + case 3: + C_front_count++ + case 4: + D_front_count++ + default: + A_front_count++ + } + } else { + switch checkedValInt { + case 2: + B_after_count++ + case 3: + C_after_count++ + case 4: + D_after_count++ + default: + A_after_count++ + } + } + } + + fmt.Printf("A_front_count: %v, B_front_count: %v, C_front_count: %v, D_front_count: %v\n", A_front_count, B_front_count, C_front_count, D_front_count) + fmt.Printf("A_after_count: %v, B_after_count: %v, C_after_count: %v, D_after_count: %v\n", A_after_count, B_after_count, C_after_count, D_after_count) + + RedColor := A_front_count + D_after_count + BlueColor := B_front_count + C_after_count + YellowColor := C_front_count + B_after_count + GreenColor := D_front_count + A_after_count + + // fmt.Printf("A_front_count: %v, D_after_count: %v, B_front_count: %v, BlueColor: %v\n", A_front_count, D_after_count, B_front_count, C_after_count) + // fmt.Printf("RedColor: %v, GreenColor: %v, YellowColor: %v, BlueColor: %v\n", RedColor, GreenColor, YellowColor, BlueColor) + + sendData := make(map[string]int) + sendData["RedColor"] = RedColor + sendData["BlueColor"] = BlueColor + sendData["YellowColor"] = YellowColor + sendData["GreenColor"] = GreenColor + sendData["A_front_count"] = A_front_count + sendData["B_front_count"] = B_front_count + sendData["C_front_count"] = C_front_count + sendData["D_front_count"] = D_front_count + sendData["A_after_count"] = A_after_count + sendData["B_after_count"] = B_after_count + sendData["C_after_count"] = C_after_count + sendData["D_after_count"] = D_after_count + return sendData +} + +// 计算颜色 +func JiSuanColorStr(testPage []TestPageColorStr) map[string]int { + if len(testPage) < 1 { + sendDataw := make(map[string]int) + sendDataw["RedColor"] = 0 + sendDataw["BlueColor"] = 0 + sendDataw["YellowColor"] = 0 + sendDataw["GreenColor"] = 0 + return sendDataw + } + A_front_count := 0 + B_front_count := 0 + C_front_count := 0 + D_front_count := 0 + A_after_count := 0 + B_after_count := 0 + C_after_count := 0 + D_after_count := 0 + // fmt.Printf("testPage:%v\n", testPage) + for _, v := range testPage { + testNumInt, _ := strconv.Atoi(v.TestNumber) + checkedValInt, _ := strconv.Atoi(v.CheckedVal) + // testNumInt := v.TestNumber + // checkedValInt := v.CheckedVal + if testNumInt <= 15 { + switch checkedValInt { + case 2: + B_front_count++ + case 3: + C_front_count++ + case 4: + D_front_count++ + default: + A_front_count++ + } + } else { + switch checkedValInt { + case 2: + B_after_count++ + case 3: + C_after_count++ + case 4: + D_after_count++ + default: + A_after_count++ + } + } + } + + fmt.Printf("A_front_count: %v, B_front_count: %v, C_front_count: %v, D_front_count: %v\n", A_front_count, B_front_count, C_front_count, D_front_count) + fmt.Printf("A_after_count: %v, B_after_count: %v, C_after_count: %v, D_after_count: %v\n", A_after_count, B_after_count, C_after_count, D_after_count) + + RedColor := A_front_count + D_after_count + BlueColor := B_front_count + C_after_count + YellowColor := C_front_count + B_after_count + GreenColor := D_front_count + A_after_count + + // fmt.Printf("A_front_count: %v, D_after_count: %v, B_front_count: %v, BlueColor: %v\n", A_front_count, D_after_count, B_front_count, C_after_count) + // fmt.Printf("RedColor: %v, GreenColor: %v, YellowColor: %v, BlueColor: %v\n", RedColor, GreenColor, YellowColor, BlueColor) + + sendData := make(map[string]int) + sendData["RedColor"] = RedColor + sendData["BlueColor"] = BlueColor + sendData["YellowColor"] = YellowColor + sendData["GreenColor"] = GreenColor + sendData["A_front_count"] = A_front_count + sendData["B_front_count"] = B_front_count + sendData["C_front_count"] = C_front_count + sendData["D_front_count"] = D_front_count + sendData["A_after_count"] = A_after_count + sendData["B_after_count"] = B_after_count + sendData["C_after_count"] = C_after_count + sendData["D_after_count"] = D_after_count + return sendData +} diff --git a/api/personalityTest/rongXin/type.go b/api/personalityTest/rongXinPage/type.go similarity index 91% rename from api/personalityTest/rongXin/type.go rename to api/personalityTest/rongXinPage/type.go index a814c04..1ea6029 100644 --- a/api/personalityTest/rongXin/type.go +++ b/api/personalityTest/rongXinPage/type.go @@ -1,4 +1,4 @@ -package rongxin +package rongXinPage import ( "appPlatform/overall/publicmethod" @@ -113,3 +113,12 @@ type CharacterStatis struct { Keywords string `json:"keywords"` Typekey string `json:"typekey"` } + +type TestPageColor struct { + TestNumber int `json:"testnumber"` //题号 + CheckedVal int `json:"checkedval"` //选项 +} +type TestPageColorStr struct { + TestNumber string `json:"testnumber"` //题号 + CheckedVal string `json:"checkedval"` //选项 +} diff --git a/api/shiyan/maptostruct/shiyan.go b/api/shiyan/maptostruct/shiyan.go index 88b49f6..9e3a873 100644 --- a/api/shiyan/maptostruct/shiyan.go +++ b/api/shiyan/maptostruct/shiyan.go @@ -411,15 +411,17 @@ func (a *ApiMethod) TestTable(c *gin.Context) { if requestData.Name == "" { requestData.Name = "shi_yang" } - masterFormCont := publicmethod.MapOut[string]() + // masterFormCont := publicmethod.MapOut[string]() + var masterFormCont []map[string]interface{} err = overall.CONSTANT_DB_CustomerForm.Table(requestData.Name).Find(&masterFormCont).Error if err != nil { publicmethod.Result(107, err, c) return } for k, v := range masterFormCont { - fmt.Printf("%v => %T\n", k, v) + fmt.Printf("%v => %T => %v\n", k, v, v) } + // fmt.Printf("%v => %T\n", masterFormCont, masterFormCont) } /* diff --git a/api/version1/customerApp/runAppControll.go b/api/version1/customerApp/runAppControll.go index 1c7fa8e..f8838e8 100644 --- a/api/version1/customerApp/runAppControll.go +++ b/api/version1/customerApp/runAppControll.go @@ -537,3 +537,157 @@ func (x *XiechengApp) GainAppList(id int64) { } } + +/* +* +@ 作者: 秦东 +@ 时间: 2024-11-27 08:41:06 +@ 功能: 按菜单顶级分组和表单 +@ 参数 + + # + +@ 返回值 + + # + +@ 方法原型 + + # +*/ +func (a *ApiMethod) GainMenuGroupForm(c *gin.Context) { + var requestData AppIdType + c.ShouldBindJSON(&requestData) + if requestData.Id == "" { + publicmethod.Result(1021, requestData, c) + return + } + var topIdAry []modelAppPlatform.Appmenus + gormDb := overall.CONSTANT_DB_AppPlatform.Model(&modelAppPlatform.Appmenus{}).Select("`id`,`label`").Where("`state` = 1 AND `type` = 1 AND `isLock` = 2 AND `appkey` = ? AND `parent` = ?", requestData.Id, requestData.Id) + if requestData.Types == 2 { + gormDb = gormDb.Where("`wapIsShow` = ?", 1) + } else { + gormDb = gormDb.Where("`pcIsShow` = ?", 1) + } + err := gormDb.Order("`sort` DESC").Find(&topIdAry).Error + if err != nil || len(topIdAry) < 1 { + publicmethod.Result(10001, topIdAry, c) + return + } + appKey, _ := strconv.ParseInt(requestData.Id, 10, 64) + var appMenu AppMenuList + for _, v := range topIdAry { + syncSeting.Add(1) + go appMenu.AppMenuContent(appKey, v) + } + syncSeting.Wait() + var sendData []MenuGroupList + for _, sv := range appMenu.List { + var sendInfo MenuGroupList + sendInfo.MenuAppId = sv.MenuAppId + sendInfo.MenuAppTitle = sv.MenuAppTitle + sendInfo.List = append(sendInfo.List, GainTableList(sv.SunId, requestData.Types)...) + sendData = append(sendData, sendInfo) + } + + var otherIdAry []SendAppForm + gormDbOther := overall.CONSTANT_DB_AppPlatform.Model(&modelAppPlatform.Appmenus{}).Select("`id`,`label`").Where("`state` = 1 AND `type` = 2 AND `isLock` = 2 AND `appkey` = ? AND `parent` = ?", requestData.Id, requestData.Id) + if requestData.Types == 2 { + gormDbOther = gormDbOther.Where("`wapIsShow` = ?", 1) + } else { + gormDbOther = gormDbOther.Where("`pcIsShow` = ?", 1) + } + gormDbOther.Order("`sort` DESC").Find(&otherIdAry) + if len(otherIdAry) > 0 { + for i, v := range otherIdAry { + otherIdAry[i].IdStr = strconv.FormatInt(v.Id, 10) + otherIdAry[i].AppkStr = strconv.FormatInt(v.Appkey, 10) + otherIdAry[i].ParentStr = strconv.FormatInt(v.Parent, 10) + otherIdAry[i].CreaterStr = strconv.FormatInt(v.Creater, 10) + var tableFormInfo modelAppPlatform.CustomerFormView + err := tableFormInfo.GetCont(map[string]interface{}{"`status`": 1, "`signCode`": v.Id}, "`id`", "`cfid`", "`time`", "`icon`", "`listjson`") + if err == nil { + if tableFormInfo.ListJson != "" { + otherIdAry[i].IsList = true + } else { + otherIdAry[i].IsList = false + } + otherIdAry[i].VersionId = strconv.FormatInt(tableFormInfo.Id, 10) + otherIdAry[i].TableId = strconv.FormatInt(tableFormInfo.CfId, 10) + var usInfo modelshr.PersonArchives //获取创建人 + usInfo.GetCont(map[string]interface{}{"`key`": v.Creater}, "`name`", "`number`") + otherIdAry[i].Founder = fmt.Sprintf("%v(%v)", usInfo.Name, usInfo.Number) + otherIdAry[i].CreateDate = publicmethod.UnixTimeToDay(tableFormInfo.CreaterTime, 14) + otherIdAry[i].Icon = tableFormInfo.Icon + } + } + var sendInfo MenuGroupList + sendInfo.MenuAppId = requestData.Id + sendInfo.MenuAppTitle = "默认分组" + sendInfo.List = append(sendInfo.List, otherIdAry...) + sendData = append(sendData, sendInfo) + } + publicmethod.Result(0, sendData, c) +} + +/* +* +@ 作者: 秦东 +@ 时间: 2024-11-27 10:22:16 +@ 功能: 获取操作表 +*/ +func GainTableList(parent []int64, types int) []SendAppForm { + var appMenusList []SendAppForm + gormDb := overall.CONSTANT_DB_AppPlatform.Model(&modelAppPlatform.Appmenus{}).Where("`state` = 1 AND `type` = 2 AND `isLock` = 2 AND `parent` IN ?", parent) + if types == 2 { + gormDb = gormDb.Where("`wapIsShow` = ?", 1) + } else { + gormDb = gormDb.Where("`pcIsShow` = ?", 1) + } + gormDb.Order("`sort` DESC").Find(&appMenusList) + for i, v := range appMenusList { + + appMenusList[i].IdStr = strconv.FormatInt(v.Id, 10) + appMenusList[i].AppkStr = strconv.FormatInt(v.Appkey, 10) + appMenusList[i].ParentStr = strconv.FormatInt(v.Parent, 10) + appMenusList[i].CreaterStr = strconv.FormatInt(v.Creater, 10) + + var tableFormInfo modelAppPlatform.CustomerFormView + err := tableFormInfo.GetCont(map[string]interface{}{"`status`": 1, "`signCode`": v.Id}, "`id`", "`cfid`", "`time`", "`icon`", "`listjson`") + if err == nil { + if tableFormInfo.ListJson != "" { + appMenusList[i].IsList = true + } else { + appMenusList[i].IsList = false + } + appMenusList[i].VersionId = strconv.FormatInt(tableFormInfo.Id, 10) + appMenusList[i].TableId = strconv.FormatInt(tableFormInfo.CfId, 10) + var usInfo modelshr.PersonArchives //获取创建人 + usInfo.GetCont(map[string]interface{}{"`key`": v.Creater}, "`name`", "`number`") + appMenusList[i].Founder = fmt.Sprintf("%v(%v)", usInfo.Name, usInfo.Number) + appMenusList[i].CreateDate = publicmethod.UnixTimeToDay(tableFormInfo.CreaterTime, 14) + appMenusList[i].Icon = tableFormInfo.Icon + } + } + return appMenusList +} + +/* +* +@ 作者: 秦东 +@ 时间: 2024-11-27 09:27:51 +@ 功能: 获取所有子类 +*/ +func (a *AppMenuList) AppMenuContent(appKey int64, menuInfo modelAppPlatform.Appmenus) { + defer syncSeting.Done() + var menuCont AppMenuListInfo + menuCont.MenuAppId = strconv.FormatInt(menuInfo.Id, 10) + menuCont.MenuAppTitle = menuInfo.Label + var menuSun publicmethod.GetOrgAllParent + menuSun.GetAppMenuSun(appKey, menuInfo.Id) + menuCont.SunId = append(menuCont.SunId, menuInfo.Id) + menuCont.SunId = append(menuCont.SunId, menuSun.Id...) + + a.List = append(a.List, menuCont) + fmt.Printf("获取所有子类:%v\n", a) +} diff --git a/api/version1/customerApp/type.go b/api/version1/customerApp/type.go index 04cd66e..603682e 100644 --- a/api/version1/customerApp/type.go +++ b/api/version1/customerApp/type.go @@ -250,3 +250,20 @@ type AppListCont struct { modelAppPlatform.CustomerForm SignCodeStr string `json:"signCodeStr" gorm:"-"` } + +type AppMenuList struct { + List []AppMenuListInfo `json:"list"` +} + +type AppMenuListInfo struct { + MenuAppId string `json:"menuAppId"` + MenuAppTitle string `json:"menuAppTitle"` + SunId []int64 `json:"sunId"` +} + +// 输出菜单分组及表单 +type MenuGroupList struct { + MenuAppId string `json:"id"` + MenuAppTitle string `json:"title"` + List []SendAppForm `json:"list"` +} diff --git a/api/version1/customerform/formTable.go b/api/version1/customerform/formTable.go index bc6a639..aed1b60 100644 --- a/api/version1/customerform/formTable.go +++ b/api/version1/customerform/formTable.go @@ -443,7 +443,18 @@ func MakeSql(tablename string, fieldCont AnalysisFormSubUnitClass, fieldList []R for _, v := range fieldList { if v.Field == fieldCont.WordName { isNew = false - sql = append(sql, fmt.Sprintf("ALTER TABLE `%v` MODIFY COLUMN %v %v COMMENT '%v'", tablename, fieldCont.WordName, v.Type, fieldCont.Describe)) + if fieldCont.FieldType == "bigint" { + if fieldCont.ValIsAry { + sql = append(sql, fmt.Sprintf("ALTER TABLE `%v` ADD COLUMN `%v` %v(%v) %v NOT NULL DEFAULT %v COMMENT '%v'", tablename, fieldCont.WordName, fieldCont.FieldType, fieldCont.MaxVal, unsigned, fieldCont.MinVal, "开始日期")) + endField := fmt.Sprintf("%v_end", fieldCont.WordName) + sql = append(sql, fmt.Sprintf("ALTER TABLE `%v` ADD COLUMN `%v` %v(%v) %v NOT NULL DEFAULT %v COMMENT '%v'", tablename, endField, fieldCont.FieldType, fieldCont.MaxVal, unsigned, fieldCont.MinVal, "结束日期")) + } else { + sql = append(sql, fmt.Sprintf("ALTER TABLE `%v` ADD COLUMN `%v` %v(%v) %v NOT NULL DEFAULT %v COMMENT '%v'", tablename, fieldCont.WordName, fieldCont.FieldType, fieldCont.MaxVal, unsigned, fieldCont.MinVal, fieldCont.Describe)) + } + } else { + sql = append(sql, fmt.Sprintf("ALTER TABLE `%v` MODIFY COLUMN %v %v COMMENT '%v'", tablename, fieldCont.WordName, v.Type, fieldCont.Describe)) + } + } } } else { @@ -454,10 +465,13 @@ func MakeSql(tablename string, fieldCont AnalysisFormSubUnitClass, fieldList []R case "int": sql = append(sql, fmt.Sprintf("ALTER TABLE `%v` ADD COLUMN `%v` %v(%v) %v NOT NULL DEFAULT %v COMMENT '%v'", tablename, fieldCont.WordName, fieldCont.FieldType, fieldCont.MaxVal, unsigned, fieldCont.MinVal, fieldCont.Describe)) case "bigint": - sql = append(sql, fmt.Sprintf("ALTER TABLE `%v` ADD COLUMN `%v` %v(%v) %v NOT NULL DEFAULT %v COMMENT '%v'", tablename, fieldCont.WordName, fieldCont.FieldType, fieldCont.MaxVal, unsigned, fieldCont.MinVal, fieldCont.Describe)) + if fieldCont.ValIsAry { + sql = append(sql, fmt.Sprintf("ALTER TABLE `%v` ADD COLUMN `%v` %v(%v) %v NOT NULL DEFAULT %v COMMENT '%v'", tablename, fieldCont.WordName, fieldCont.FieldType, fieldCont.MaxVal, unsigned, fieldCont.MinVal, "开始日期")) endField := fmt.Sprintf("%v_end", fieldCont.WordName) - sql = append(sql, fmt.Sprintf("ALTER TABLE `%v` ADD COLUMN `%v` %v(%v) %v NOT NULL DEFAULT %v COMMENT '%v'", tablename, endField, fieldCont.FieldType, fieldCont.MaxVal, unsigned, fieldCont.MinVal, fieldCont.Describe)) + sql = append(sql, fmt.Sprintf("ALTER TABLE `%v` ADD COLUMN `%v` %v(%v) %v NOT NULL DEFAULT %v COMMENT '%v'", tablename, endField, fieldCont.FieldType, fieldCont.MaxVal, unsigned, fieldCont.MinVal, "结束日期")) + } else { + sql = append(sql, fmt.Sprintf("ALTER TABLE `%v` ADD COLUMN `%v` %v(%v) %v NOT NULL DEFAULT %v COMMENT '%v'", tablename, fieldCont.WordName, fieldCont.FieldType, fieldCont.MaxVal, unsigned, fieldCont.MinVal, fieldCont.Describe)) } case "float": sql = append(sql, fmt.Sprintf("ALTER TABLE `%v` ADD COLUMN `%v` %v(%v,%v) %v NOT NULL DEFAULT 0 COMMENT '%v'", tablename, fieldCont.WordName, fieldCont.FieldType, fieldCont.MaxVal, fieldCont.MinVal, unsigned, fieldCont.Describe)) @@ -717,8 +731,10 @@ func (a *ApiMethod) GainFormTableField(c *gin.Context) { masterTable = append(masterTable, myKeyWord) } } + masterTableAll, err := GainFormTableField(customerFormMaster.TableKey) masterTable = append(masterTable, masterTableAll...) + if err != nil { publicmethod.Result(1, err, c, "未知表单!无法获取字段!") return @@ -734,8 +750,11 @@ func (a *ApiMethod) GainFormTableField(c *gin.Context) { if listAry, ok := list.([]interface{}); ok { formFieldAry.AnalyzingFormJson("", listAry) + // publicmethod.Result(0, listAry, c) + // return } } + sendTableList := publicmethod.MapOut[string]() if len(formFieldAry.MasterInfo) > 0 { sendTableList["masterTable"] = TableFieldCompare(masterTable, formFieldAry.MasterInfo) @@ -823,12 +842,12 @@ func GainSunFormTableField(tableName string, SunFormInfo []SunFormFieldInfoList) */ func TableFieldCompare(tableFieldList []Result, jsonFieldList []FormFieldInfo) (fieldList []FormFieldInfo) { - tableFieldListsdf, _ := json.Marshal(tableFieldList) - jsonFieldListsdf, _ := json.Marshal(jsonFieldList) - fmt.Printf("\n=====================================================\n") - fmt.Printf("tableFieldListsdf:%v\n", string(tableFieldListsdf)) - fmt.Printf("jsonFieldListsdf:%v\n", string(jsonFieldListsdf)) - fmt.Printf("\n=====================================================\n") + // tableFieldListsdf, _ := json.Marshal(tableFieldList) + // jsonFieldListsdf, _ := json.Marshal(jsonFieldList) + // fmt.Printf("\n=====================================================\n") + // fmt.Printf("tableFieldListsdf:%v\n", string(tableFieldListsdf)) + // fmt.Printf("jsonFieldListsdf:%v\n", string(jsonFieldListsdf)) + // fmt.Printf("\n=====================================================\n") if len(tableFieldList) > 0 { for _, v := range tableFieldList { @@ -933,6 +952,7 @@ func (f *FormJsonFieldInfo) AnalyzingFormJson(tableName string, unitList []inter if len(unitList) > 0 { var fieldInfo []FormFieldInfo for _, listVal := range unitList { + // fmt.Printf("listVal------------------>%v\n", listVal) if listInfo, ok := listVal.(map[string]interface{}); ok { if unitType, ok := listInfo["type"]; ok { var unitInfo FormFieldInfo @@ -1049,10 +1069,20 @@ func (f *FormJsonFieldInfo) AnalyzingFormJson(tableName string, unitList []inter optInfo.Label = labelValStr } } + if valueVal, ok := optvMap["value"]; ok { - if valueValStr, ok := valueVal.(string); ok { - optInfo.Value = valueValStr - } + optInfo.Value = valueVal + // if valueValStr, ok := valueVal.(string); ok { + // optInfo.Value = valueValStr + // } else { + // intVal, _ := publicmethod.StringToInt64(valueVal) + // optInfo.Value = intVal + // } + } + if chiVal, ok := optvMap["children"]; ok { + // fmt.Printf("unitInfo--3->:%v---->%T\n", chiVal, chiVal) + optInfo.Children = chiVal + } unitInfo.Options = append(unitInfo.Options, optInfo) } @@ -1510,7 +1540,9 @@ func (l *ListPageFields) BaseTableAttrField(key string, val interface{}) interfa if len(checkboxAry) > 0 { var jieGuo []string for _, ov := range v.Options { - if publicmethod.IsInTrue[string](ov.Value, checkboxAry) { + intVal, _ := publicmethod.StringToInt64(ov.Value) + intValStr := strconv.FormatInt(intVal, 10) + if publicmethod.IsInTrue[string](intValStr, checkboxAry) { jieGuo = append(jieGuo, ov.Label) } } diff --git a/api/version1/customerform/formTableView.go b/api/version1/customerform/formTableView.go index 36b1b3e..bbf8cd0 100644 --- a/api/version1/customerform/formTableView.go +++ b/api/version1/customerform/formTableView.go @@ -566,7 +566,9 @@ func (l *ListPageFields) BaseTableAttrViewField(key string, val interface{}, lis if len(checkboxAry) > 0 { var jieGuo []string for _, ov := range v.Options { - if publicmethod.IsInTrue[string](ov.Value, checkboxAry) { + intVal, _ := publicmethod.StringToInt64(ov.Value) + intValStr := strconv.FormatInt(intVal, 10) + if publicmethod.IsInTrue[string](intValStr, checkboxAry) { jieGuo = append(jieGuo, ov.Label) } } @@ -1134,7 +1136,7 @@ func (a *ApiMethod) GainFormPageMapCont(c *gin.Context) { func TableFormAttribute(formField Result, tableFieldList []FormFieldInfo) (fieldInfo FormFieldInfo, isTrue bool) { if len(tableFieldList) > 0 { fieldAry := strings.Split(formField.Field, "_end") - fmt.Printf("字段数组----->%v----->%v\n", len(fieldAry), fieldAry) + // fmt.Printf("字段数组----->%v----->%v\n", len(fieldAry), fieldAry) if len(fieldAry) >= 1 { isWrite := true for _, v := range tableFieldList { @@ -1182,7 +1184,7 @@ func TableFormAttribute(formField Result, tableFieldList []FormFieldInfo) (field // if len(patternInfo) >= 1 { // fieldInfo.Pattern = patternInfo[0] // } - fmt.Printf("字段数组---12-->%v----->%v\n", isWrite, formField) + // fmt.Printf("字段数组---12-->%v----->%v\n", isWrite, formField) switch formField.Field { case "_lableTitle": fieldInfo.Id = formField.Field diff --git a/api/version1/customerform/type.go b/api/version1/customerform/type.go index 8089207..7489c25 100644 --- a/api/version1/customerform/type.go +++ b/api/version1/customerform/type.go @@ -456,8 +456,9 @@ type ControlType struct { } type OptionsInfo struct { - Label string `json:"label"` - Value string `json:"value"` + Label string `json:"label"` + Value interface{} `json:"value"` + Children interface{} `json:"children"` } // 分析表单Json字段 diff --git a/api/version1/entry.go b/api/version1/entry.go index af6597d..32a2e7c 100644 --- a/api/version1/entry.go +++ b/api/version1/entry.go @@ -16,6 +16,7 @@ import ( "appPlatform/api/version1/taskplatform/taskmanagement" "appPlatform/api/version1/user" webstocetmsg "appPlatform/api/version1/webStocetmsg" + workflow "appPlatform/api/version1/workFlow" "appPlatform/api/version1/workWechat" ) @@ -36,6 +37,7 @@ type ApiEntry struct { DataCenterApi datacenter.ApiMethod //数据中台 CustomerAppApi customerApp.ApiMethod //自定App WebSocketApi webstocetmsg.ApiMethod //webSocket通讯相关 + WorkFlowApi workflow.ApiMethod //工作流相关 } var AppApiEntry = new(ApiEntry) diff --git a/api/version1/publicapi/api.go b/api/version1/publicapi/api.go index 3cd3714..4e325c1 100644 --- a/api/version1/publicapi/api.go +++ b/api/version1/publicapi/api.go @@ -358,7 +358,7 @@ func (a *ApiMethod) GainNumber(c *gin.Context) { var requestData GainRulsNumner c.ShouldBindJSON(&requestData) uuid := strconv.FormatInt(publicmethod.GetUUid(1), 10) - // fmt.Printf("uuidAry---->%v\n", uuid) + // fmt.Printf("uuidAry---->%v\n", requestData) if requestData.Automatic { uuid = requestData.GainUniqueNumber(uuid) } @@ -385,6 +385,8 @@ func (a *ApiMethod) GainNumber(c *gin.Context) { */ func (g *GainRulsNumner) GainUniqueNumber(uuid string) string { var uuidAry []string + // fmt.Printf("获取唯一编号%v\n", g) + // fmt.Printf("uuidAry---->%v\n", uuid) if len(g.CustomRules) > 0 { for _, v := range g.CustomRules { switch v.Types { @@ -470,6 +472,7 @@ func (g *GainRulsNumner) GainUniqueNumber(uuid string) string { } g.GainUniqueNumber(uuid) } + return uuid } diff --git a/api/version1/taskplatform/taskmanagement/flowType.go b/api/version1/taskplatform/taskmanagement/flowType.go index 376ace4..06bef7e 100644 --- a/api/version1/taskplatform/taskmanagement/flowType.go +++ b/api/version1/taskplatform/taskmanagement/flowType.go @@ -11,17 +11,26 @@ type StartWorkFlow struct { // 流程执行 type RunWorkFlow struct { - Step int //执行哪一步 - NextStep int //下一步 - TotalSteps int //总步数 - FlowList []RunFlow //流程 - Participant []string //参与人 - MakeCopy []string //抄送人 - NewFlowList []RunFlow //流程 - Uuid int64 // - RunUid int64 //执行Uid - IsRun bool //是否结束执行 - Msg string //执行说明 + Step int //执行哪一步 + NextStep int //下一步 + TotalSteps int //总步数 + FlowList []RunFlow //流程 + Participant []string //参与人 + MakeCopy []string //抄送人 + NewFlowList []RunFlow //流程 + Uuid int64 // + RunUid int64 //执行Uid + IsRun bool //是否结束执行 + Msg string //执行说明 + SendWecharMsg SendWecharMsgMap +} + +type SendWecharMsgMap struct { + MastersKey int64 + TableKey int64 + AppKey int64 + RunFlowId int64 + Creater int64 } type SubmitAppResults struct { publicmethod.PublicId diff --git a/api/version1/taskplatform/taskmanagement/runTaskFlow.go b/api/version1/taskplatform/taskmanagement/runTaskFlow.go index 8cfe56f..96175cc 100644 --- a/api/version1/taskplatform/taskmanagement/runTaskFlow.go +++ b/api/version1/taskplatform/taskmanagement/runTaskFlow.go @@ -75,6 +75,9 @@ func (a *ApiMethod) RunTaskFlow(c *gin.Context) { context, _ := c.Get(overall.MyContJwt) var userCont modelshr.ManCont userCont.GetLoginCont(context) //当前操作人 + + var taskInfo customerForm.TaskRecord + taskInfo.GetCont(map[string]interface{}{"`runFlowId`": requestData.Id}, "`title`", "`creater`", `masters_key`, "`version_id`", "`flow_key`", "`flow_run_sing`", "`tableKey`", "`appKey`", "`runFlowId`") //执行流程 var runFlow RunWorkFlow runFlow.Step = flowInfo.NextStep @@ -83,6 +86,13 @@ func (a *ApiMethod) RunTaskFlow(c *gin.Context) { runFlow.RunUid = flowInfo.RunKey runFlow.Participant = strings.Split(flowInfo.Participants, ",") runFlow.FlowList = requestData.FlowList + + runFlow.SendWecharMsg.AppKey = taskInfo.AppKey + runFlow.SendWecharMsg.Creater = taskInfo.Creater + runFlow.SendWecharMsg.MastersKey = taskInfo.MastersKey + runFlow.SendWecharMsg.TableKey = taskInfo.TableKey + runFlow.SendWecharMsg.RunFlowId = taskInfo.RunFlowId + runFlow.RunNodeHandle(userCont.Key, requestData.AgreeOrRefuse, requestData.Suggest) if runFlow.IsRun { publicmethod.Result(1, err, c, runFlow.Msg) @@ -191,7 +201,7 @@ func (r *RunWorkFlow) RunNodeHandle(userKey int64, AgreeToRefuse int, Suggest st return } userKeyStr := strconv.FormatInt(userKey, 10) //当前操作人识别符转字符类型 - // fmt.Printf("执行节点处理--->%v------>%v\n", r.Step, r.NextStep) + fmt.Printf("执行节点处理--->%v------>%v\n", r.Step, r.NextStep) //遍历流程判断当前要执行的步骤 for i := 0; i < r.TotalSteps; i++ { if r.FlowList[i].Step == r.Step { //获取当前操作节点 @@ -205,10 +215,12 @@ func (r *RunWorkFlow) RunNodeHandle(userKey int64, AgreeToRefuse int, Suggest st FlowRunLog(r.Uuid, userkIntId, AgreeToRefuse, r.FlowList[i].NodeKey, title, "") // fmt.Printf("抄送人---->%v---->%v\n", userkIntId, title) } + r.SendWecharMsgTopct(userKeyStr, r.Step) r.Step = currentStep r.NextStep = nextStep if nextStep > 0 { - if JudgeRunNode(nextStep, r.FlowList) { + // if JudgeRunNode(nextStep, r.FlowList) { + if r.ToNextNodeRunOrClose(userKeyStr, nextStep) { r.Step = nextStep r.RunNodeHandle(userKey, AgreeToRefuse, Suggest) } @@ -224,6 +236,7 @@ func (r *RunWorkFlow) RunNodeHandle(userKey int64, AgreeToRefuse int, Suggest st r.FlowList[i].Operator = operatorAry r.Participant = append(r.Participant, nodeUser...) r.RejectNode(r.FlowList[i].GoBackNode) + r.SendWecharMsgTextCard(r.Step, "驳回", Suggest) return } else { currentStep, nextStep := PaceStep(r.RunUid, r.Step, r.TotalSteps, r.FlowList[i]) @@ -234,23 +247,32 @@ func (r *RunWorkFlow) RunNodeHandle(userKey int64, AgreeToRefuse int, Suggest st r.Participant = append(r.Participant, nodeUser...) switch r.FlowList[i].Types { case 0: + if r.Step != currentStep { + r.SendWecharMsgTopct(userKeyStr, currentStep) + } r.Step = currentStep r.NextStep = nextStep FlowRunLog(r.Uuid, userKey, 2, r.FlowList[i].NodeKey, "发起流程", "") - if JudgeRunNode(nextStep, r.FlowList) { + // if JudgeRunNode(nextStep, r.FlowList) { + if r.ToNextNodeRunOrClose(userKeyStr, nextStep) { r.Step = nextStep r.FlowStepRun(userKey, AgreeToRefuse, Suggest) } return case 1: + if r.Step != currentStep { + r.SendWecharMsgTopct(userKeyStr, currentStep) + } r.Step = currentStep r.NextStep = nextStep // fmt.Printf("判断是否继续执行---->%v\n", JudgeRunNode(nextStep, r.FlowList)) FlowRunLog(r.Uuid, userKey, 2, r.FlowList[i].NodeKey, Suggest, "") - if JudgeRunNode(nextStep, r.FlowList) { + if r.ToNextNodeRunOrClose(userKeyStr, nextStep) { + // if JudgeRunNode(nextStep, r.FlowList) { r.Step = nextStep r.RunNodeHandle(userKey, AgreeToRefuse, Suggest) } + return case 2: if currentStep+1 < r.TotalSteps { r.Step = currentStep + 1 @@ -269,14 +291,21 @@ func (r *RunWorkFlow) RunNodeHandle(userKey int64, AgreeToRefuse int, Suggest st title := fmt.Sprintf("向%v发送抄送数据", op.Name) FlowRunLog(r.Uuid, userkIntId, AgreeToRefuse, r.FlowList[i].NodeKey, title, "") } + r.SendWecharMsgTopct(userKeyStr, r.Step) + return case 3: + if r.Step != currentStep { + r.SendWecharMsgTopct(userKeyStr, currentStep) + } r.Step = currentStep r.NextStep = nextStep FlowRunLog(r.Uuid, userKey, 2, r.FlowList[i].NodeKey, Suggest, "") - if JudgeRunNode(nextStep, r.FlowList) { + if r.ToNextNodeRunOrClose(userKeyStr, nextStep) { + // if JudgeRunNode(nextStep, r.FlowList) { r.Step = nextStep r.RunNodeHandle(userKey, AgreeToRefuse, Suggest) } + return default: } diff --git a/api/version1/taskplatform/taskmanagement/runWorkFlow.go b/api/version1/taskplatform/taskmanagement/runWorkFlow.go index db93cca..973abdc 100644 --- a/api/version1/taskplatform/taskmanagement/runWorkFlow.go +++ b/api/version1/taskplatform/taskmanagement/runWorkFlow.go @@ -41,14 +41,15 @@ func (a *ApiMethod) StartRunWorkFlow(c *gin.Context) { publicmethod.Result(10001, err, c, "未知进程!不可执行") return } + var taskInfo customerForm.TaskRecord if requestData.Id == "" { - var taskInfo customerForm.TaskRecord - taskInfo.EiteCont(map[string]interface{}{"`masters_key`": requestData.Id}, map[string]interface{}{"`status`": 1}) + //获取当前任务内容 + // taskInfo.EiteCont(map[string]interface{}{"`masters_key`": requestData.Id}, map[string]interface{}{"`status`": 1}) publicmethod.Result(10001, err, c, "未知进程!不可执行") return } if len(requestData.FlowList) < 1 { - var taskInfo customerForm.TaskRecord + //获取当前任务内容 taskInfo.EiteCont(map[string]interface{}{"`masters_key`": requestData.Id}, map[string]interface{}{"`status`": 1}) publicmethod.Result(10001, err, c, "未知进程!不可执行") return @@ -60,8 +61,8 @@ func (a *ApiMethod) StartRunWorkFlow(c *gin.Context) { var userCont modelshr.ManCont userCont.GetLoginCont(context) //当前操作人 creetTime := time.Now().Unix() //当前时间 - //获取当前任务内容 - var taskInfo customerForm.TaskRecord + // //获取当前任务内容 + // var taskInfo customerForm.TaskRecord taskInfo.GetCont(map[string]interface{}{"`masters_key`": requestData.Id}, "`flow_key`", "`flow_run_sing`") //获取流程详情 var flowVersionInfo modelAppPlatform.WorkFlowVersion @@ -283,6 +284,86 @@ func JudgeRunNode(step int, FlowList []RunFlow) (isRun bool) { return } +/* +* +@ 作者: 秦东 +@ 时间: 2024-12-13 11:26:32 +@ 功能: 判断下一个节点是否继续执行 +@ 参数 + + #userKey 当前执行人 + #nextStep 下一个节点 + +@ 返回值 + + # + +@ 方法原型 + + # +*/ +func (r *RunWorkFlow) ToNextNodeRunOrClose(userKey string, nextStep int) (isRun bool) { + isRun = false + if nextStep == 0 { + return isRun + } + runIdStr := strconv.FormatInt(r.RunUid, 10) + fmt.Printf("判断下一个节点是否继续执行:userKey:%v------->nextStep:%v\n", userKey, nextStep) + for _, v := range r.FlowList { + if nextStep == v.Step { + fmt.Printf("判断下一个节点是否继续执行:v.Step:%v----1--->v.Types:%v\n", v.Step, v.Types) + switch v.Types { + case 1, 3: + isMyTrue := false + optUserCount := len(v.Operator) + allPick := 0 + for _, ov := range v.Operator { + if ov.Id == userKey { + isMyTrue = true + } + for _, ol := range ov.LogList { + if runIdStr == ol.UID { + allPick++ + } + } + } + if isMyTrue { + if optUserCount > 1 { + switch v.ExamineMode { + case 1, 2: + if allPick == 0 { + isRun = true + } else { + if allPick == optUserCount { + isRun = true + } else { + isRun = false + } + } + + default: + isRun = true + } + } else { + if optUserCount == 1 { + isRun = true + } else { + isRun = false + } + } + // isRun = true + } + fmt.Printf("判断下一个节点是否继续执行:isMyTrue:%v----3--->isRun:%v\n----3--->optUserCount:%v\n----3--->v.ExamineMode:%v\n", isMyTrue, isRun, optUserCount, v.ExamineMode) + case 2: + isRun = true + default: + isRun = false + } + } + } + return isRun +} + /* * @ 作者: 秦东 @@ -1571,15 +1652,23 @@ func JudgeOperUser(userKey, runId int64, Operator []OperatorList) (bool, string) } caoZuoQuanXian := true for _, v := range Operator { + if v.Id == strconv.FormatInt(userKey, 10) { + js, _ := json.Marshal(v) + fmt.Printf("判断操作人是否已经操作过\n userKey:%v====runId:%v=========v.Id=>%v\n\n\n%v\n\n\n", userKey, runId, v.Id, string(js)) caoZuoQuanXian = false if len(v.LogList) > 0 { for _, m := range v.LogList { if m.UID == strconv.FormatInt(runId, 10) { + fmt.Printf("判断操作人\n\n\n userKey:%v====runId:%v=========v.Id=>%v\n\n\n%v\n\n\n", userKey, runId, m.UID, m) return true, "您已经操作过此节点!请不要重复提交!" } } + } else { + caoZuoQuanXian = false } + jsLog, _ := json.Marshal(v.LogList) + fmt.Printf("是否已经操作过\n userKey:%v====runId:%v=========caoZuoQuanXian=>%v\n\n\n%v\n\n\n", userKey, runId, caoZuoQuanXian, string(jsLog)) } } if caoZuoQuanXian { diff --git a/api/version1/taskplatform/taskmanagement/sendWechatMsg.go b/api/version1/taskplatform/taskmanagement/sendWechatMsg.go new file mode 100644 index 0000000..8a740fd --- /dev/null +++ b/api/version1/taskplatform/taskmanagement/sendWechatMsg.go @@ -0,0 +1,272 @@ +package taskmanagement + +import ( + "appPlatform/api/version1/customerform" + "appPlatform/api/version1/workWechat" + "appPlatform/models/modelAppPlatform" + "appPlatform/models/modelshr" + "appPlatform/overall" + "appPlatform/overall/publicmethod" + "encoding/json" + "fmt" + "strconv" + "strings" + "time" +) + +/* +* +@ 作者: 秦东 +@ 时间: 2024-12-13 13:38:52 +@ 功能: 向下一个节点人员发送消息 +@ 参数 + + # + +@ 返回值 + + # + +@ 方法原型 + + # +*/ +func (r *RunWorkFlow) SendWecharMsgTopct(userKey string, nextStep int) { + if nextStep > 0 { + var sendUserAry []string + nodeTitle := "" + for _, v := range r.FlowList { + if nextStep == v.Step { + nodeTitle = v.NodeName + if v.ExamineMode != 1 { + for _, ov := range v.Operator { + if userKey != "" { + if ov.Id != userKey && ov.Wechat != "" { + sendUserAry = append(sendUserAry, ov.Wechat) + } + } else { + if ov.Wechat != "" { + sendUserAry = append(sendUserAry, ov.Wechat) + } + } + + } + } else { + uuidStr := strconv.FormatInt(r.Uuid, 10) + for _, ov := range v.Operator { + for _, lv := range ov.LogList { + if lv.UID != uuidStr { + if userKey != "" { + if ov.Id != userKey && ov.Wechat != "" { + sendUserAry = append(sendUserAry, ov.Wechat) + } + } else { + if ov.Wechat != "" { + sendUserAry = append(sendUserAry, ov.Wechat) + } + } + } + } + } + } + + } + } + if len(sendUserAry) > 0 { + taskId := publicmethod.GetUUid(1) + tableName, qouteTitle, contList := r.GainTitleAndCreate() + var sendMsg workWechat.SendMessage + sendMsg.Touser = strings.Join(sendUserAry, "|") + sendMsg.TemplateCard.Source.Desc = nodeTitle + sendMsg.TemplateCard.MainTitle.Title = tableName + sendMsg.TemplateCard.TaskId = strconv.FormatInt(taskId, 10) + sendMsg.TemplateCard.QuoteArea.Quote_text = qouteTitle + sendMsg.TemplateCard.HorizontalContentList = append(sendMsg.TemplateCard.HorizontalContentList, contList...) + var jumpInfo workWechat.Jump_list + jumpInfo.Title = "前往处理" + jumpInfo.Url = "wap.gyhlw.group" + jumpInfo.Type = 1 + sendMsg.TemplateCard.JumpList = append(sendMsg.TemplateCard.JumpList, jumpInfo) + sendMsg.TemplateCard.CardAction.Type = 1 + sendMsg.TemplateCard.CardAction.Url = "wap.gyhlw.group" + sendMsg.SendMsg("stzl", 1) + } + } +} + +/* +* +@ 作者: 秦东 +@ 时间: 2024-12-17 15:53:24 +@ 功能: 发送文本卡片消息 +@ 参数 + + #userKey 接收人 + +@ 返回值 + + # + +@ 方法原型 + + # +*/ +func (r *RunWorkFlow) SendWecharMsgTextCard(runStep int, nodeTitle, content string) { + if runStep == 0 { + runStep = 1 + } + if runStep > len(r.FlowList) { + runStep = len(r.FlowList) + } + var sendUser []string + for _, v := range r.FlowList { + if v.Step == runStep { + for _, lv := range v.Operator { + if lv.Wechat != "" && !publicmethod.IsInTrue[string](lv.Wechat, sendUser) { + sendUser = append(sendUser, lv.Wechat) + } + } + } + } + // fmt.Printf("驳回数据接收人:%v", sendUser) + if len(sendUser) > 0 { + tableName, qouteTitle, _ := r.GainTitleAndCreate() + var sendMsg workWechat.SendMessage + sendMsg.Touser = strings.Join(sendUser, "|") + sendMsg.Msgtype = "textcard" + sendMsg.Textcard.Title = nodeTitle + sendMsg.Textcard.Description = fmt.Sprintf("
%v
%v
%v
驳回原因:%v
", publicmethod.UnixTimeToDay(time.Now().Unix(), 11), tableName, qouteTitle, content) + sendMsg.Textcard.Url = "wap.gyhlw.group" + sendMsg.Textcard.Btntxt = "前往查看" + fmt.Printf("驳回数据接收人2:%v", sendMsg) + sendMsg.SendMsg("stzl", 1) + } +} + +/* +* +@ 作者: 秦东 +@ 时间: 2024-12-13 15:38:55 +@ 功能: 获取指定标题和申请人相关信息 + +#tableName 一级标题 +#qouteTitle 引用文件 +#peopleInfoAry 发起人 +*/ +func (r *RunWorkFlow) GainTitleAndCreate() (tableName, qouteTitle string, peopleInfoAry []workWechat.Horizontal_content_list) { + var tableInFor modelAppPlatform.CustomerForm + tableInFor.GetCont(map[string]interface{}{"`signCode`": r.SendWecharMsg.TableKey}, "`name`", "`tablename`", "`listjson`") + tableName = tableInFor.Name + qouteTitle = tableInFor.Name + var ceraterInfo modelshr.PersonArchives + ceraterInfo.GetCont(map[string]interface{}{"`key`": r.SendWecharMsg.Creater}, "`name`", "`number`", "`wechat`", "`work_wechat`") + var peopleInfo workWechat.Horizontal_content_list + peopleInfo.Keyname = "申请人" + peopleInfo.Value = fmt.Sprintf("%v(%v)", ceraterInfo.Name, ceraterInfo.Number) + if ceraterInfo.WorkWechat != "" { + peopleInfo.Type = 3 + peopleInfo.UserId = ceraterInfo.WorkWechat + } else if ceraterInfo.Wechat != "" { + peopleInfo.Type = 3 + peopleInfo.UserId = ceraterInfo.Wechat + } + peopleInfoAry = append(peopleInfoAry, peopleInfo) + if tableInFor.ListJson != "" { + var listInfo customerform.ListPageFields + json.Unmarshal([]byte(tableInFor.ListJson), &listInfo) + if listView, isOk := listInfo.View["list"]; isOk { + if len(listView.Form.Title) > 0 { + tableForm := publicmethod.MapOut[string]() + overall.CONSTANT_DB_CustomerForm.Table(tableInFor.TableNames).Where("`masters_key` = ?", r.SendWecharMsg.MastersKey).Find(&tableForm) + var qouteTitleAry []string + // fmt.Printf("list表单:%v\n\n\n%v\n\n\n", listView.Form.Title, tableForm) + for _, v := range listView.Form.Title { + if formVal, isOk := tableForm[v]; isOk { + // fmt.Printf("list表单-----v----->:%v\n\n\n%v\n\n\n", formVal, publicmethod.TypeToInterface(formVal)) + qouteTitleAry = append(qouteTitleAry, publicmethod.TypeToInterface(formVal)) + } + } + if len(qouteTitleAry) > 0 { + qouteTitle = strings.Join(qouteTitleAry, "-") + } + } + } else if dateView, isOk := listInfo.View["date"]; isOk { + if len(dateView.Form.Title) > 0 { + tableForm := publicmethod.MapOut[string]() + overall.CONSTANT_DB_CustomerForm.Table(tableInFor.TableNames).Where("`masters_key` = ?", r.SendWecharMsg.MastersKey).Find(&tableForm) + var qouteTitleAry []string + // fmt.Printf("date表单:%v\n\n\n%v\n\n\n", dateView.Form.Title, tableForm) + for _, v := range dateView.Form.Title { + if formVal, isOk := tableForm[v]; isOk { + qouteTitleAry = append(qouteTitleAry, publicmethod.TypeToInterface(formVal)) + } + } + if len(qouteTitleAry) > 0 { + qouteTitle = strings.Join(qouteTitleAry, "-") + } + } + } else if timeView, isOk := listInfo.View["time"]; isOk { + if len(timeView.Form.Title) > 0 { + tableForm := publicmethod.MapOut[string]() + overall.CONSTANT_DB_CustomerForm.Table(tableInFor.TableNames).Where("`masters_key` = ?", r.SendWecharMsg.MastersKey).Find(&tableForm) + var qouteTitleAry []string + // fmt.Printf("time表单:%v\n\n\n%v\n\n\n", timeView.Form.Title, tableForm) + for _, v := range timeView.Form.Title { + if formVal, isOk := tableForm[v]; isOk { + qouteTitleAry = append(qouteTitleAry, publicmethod.TypeToInterface(formVal)) + } + } + if len(qouteTitleAry) > 0 { + qouteTitle = strings.Join(qouteTitleAry, "-") + } + } + } else if ganttView, isOk := listInfo.View["gantt"]; isOk { + if len(ganttView.Form.Title) > 0 { + tableForm := publicmethod.MapOut[string]() + overall.CONSTANT_DB_CustomerForm.Table(tableInFor.TableNames).Where("`masters_key` = ?", r.SendWecharMsg.MastersKey).Find(&tableForm) + var qouteTitleAry []string + // fmt.Printf("gantt表单:%v\n\n\n%v\n\n\n", ganttView.Form.Title, tableForm) + for _, v := range ganttView.Form.Title { + if formVal, isOk := tableForm[v]; isOk { + qouteTitleAry = append(qouteTitleAry, publicmethod.TypeToInterface(formVal)) + } + } + if len(qouteTitleAry) > 0 { + qouteTitle = strings.Join(qouteTitleAry, "-") + } + } + } else if mapView, isOk := listInfo.View["map"]; isOk { + if len(mapView.Form.Title) > 0 { + tableForm := publicmethod.MapOut[string]() + overall.CONSTANT_DB_CustomerForm.Table(tableInFor.TableNames).Where("`masters_key` = ?", r.SendWecharMsg.MastersKey).Find(&tableForm) + var qouteTitleAry []string + // fmt.Printf("map表单:%v\n\n\n%v\n\n\n", mapView.Form.Title, tableForm) + for _, v := range mapView.Form.Title { + if formVal, isOk := tableForm[v]; isOk { + qouteTitleAry = append(qouteTitleAry, publicmethod.TypeToInterface(formVal)) + } + } + if len(qouteTitleAry) > 0 { + qouteTitle = strings.Join(qouteTitleAry, "-") + } + } + } else if cardView, isOk := listInfo.View["card"]; isOk { + if len(cardView.Form.Title) > 0 { + tableForm := publicmethod.MapOut[string]() + overall.CONSTANT_DB_CustomerForm.Table(tableInFor.TableNames).Where("`masters_key` = ?", r.SendWecharMsg.MastersKey).Find(&tableForm) + var qouteTitleAry []string + // fmt.Printf("card表单:%v\n\n\n%v\n\n\n", cardView.Form.Title, tableForm) + for _, v := range cardView.Form.Title { + if formVal, isOk := tableForm[v]; isOk { + qouteTitleAry = append(qouteTitleAry, publicmethod.TypeToInterface(formVal)) + } + } + if len(qouteTitleAry) > 0 { + qouteTitle = strings.Join(qouteTitleAry, "-") + } + } + } + } + // fmt.Printf("qouteTitle表单:%v\n\n\n", qouteTitle) + return +} diff --git a/api/version1/workFlow/common.go b/api/version1/workFlow/common.go new file mode 100644 index 0000000..2248765 --- /dev/null +++ b/api/version1/workFlow/common.go @@ -0,0 +1,1061 @@ +package workflow + +import ( + "appPlatform/api/version1/customerform" + "appPlatform/api/version1/taskplatform/taskmanagement" + "appPlatform/api/version1/workWechat" + "appPlatform/models/modelAppPlatform" + "appPlatform/models/modelshr" + "appPlatform/overall" + "appPlatform/overall/publicmethod" + "encoding/json" + "fmt" + "strconv" + "strings" + "time" + + "github.com/gin-gonic/gin" +) + +// 入口函数 +func (a *ApiMethod) Index(c *gin.Context) { + outputCont := publicmethod.MapOut[string]() + outputCont["index"] = "工作流入口" + publicmethod.Result(0, outputCont, c) +} + +/* +* +@ 作者: 秦东 +@ 时间: 2024-12-12 16:46:21 +@ 功能: 执行工作流 +@ 参数 + + #userKey //当前执行人 + #AgreeToRefuse //同意或者驳回 + #runCont //执行意见 + +@ 返回值 + + # + +@ 方法原型 + + # +*/ +func (r *RunWorkFlow) GainRunNodeNew(userKey string, runSetup, AgreeToRefuse int, runCont string) (currentStep, nextStep int) { + + if len(r.FlowList) > 0 { + userkInt, _ := strconv.ParseInt(userKey, 10, 64) //将字符型转换成64位整型 + for i := 0; i < r.TotalSteps; i++ { //循环比对获取出当前执行节点 + if r.FlowList[i].Step == runSetup { //获取当前执行节点内容 + fmt.Printf("执行工作流当前节点-1-r.FlowList[i].Step:%v\n---1--》r.Step:%v\n", r.FlowList[i].Step, runSetup) + r.FlowList[i].Status = AgreeToRefuse //节点状态 + currentStep, nextStep = r.TallyPaceStepes(userKey, runSetup, r.FlowList[i]) + fmt.Printf("执行工作流当前节点-2-currentStep:%v\n--2---》nextStep:%v\n", currentStep, nextStep) + //获取参与人几当前节点操作记录 + operatorAry, nodeUser := CopeParticipantEnforcerLog(userKey, r.RunUid, AgreeToRefuse, r.FlowList[i].Operator, runCont) + r.FlowList[i].Operator = operatorAry //节点操作记录 + r.Participant = append(r.Participant, nodeUser...) //参与人 + // panic("中断一下") + switch r.FlowList[i].Types { //0:发起节点;1:审批节点;2:抄送;3:执行节点 + case 0: + + taskmanagement.FlowRunLog(r.Uuid, userkInt, AgreeToRefuse, r.FlowList[i].NodeKey, runCont, "") + + //判断下一个节点是否满足自动审批条件 + if r.ToNextNodeRunOrClose(userKey, nextStep) { + // r.Step = r.NextStep + fmt.Printf("执行下一节点--r.Step:%v\n----->r.NextStep:%v\n", r.Step, nextStep) + currentStep, nextStep = r.GainRunNodeNew(userKey, nextStep, AgreeToRefuse, "自动审批") + } else { + //判断是否向下一个节点的人发送信息 + r.SendWecharMsgTopct(userKey, nextStep) + } + r.Step = currentStep + r.NextStep = nextStep + return + case 1, 3: + + taskmanagement.FlowRunLog(r.Uuid, userkInt, AgreeToRefuse, r.FlowList[i].NodeKey, runCont, "") + fmt.Printf("执行下一节点-34324234-currentStep:%v\n----->nextStep:%v\n", currentStep, nextStep) + //判断下一个节点是否满足自动审批条件 + if r.ToNextNodeRunOrClose(userKey, nextStep) { + // r.Step = r.NextStep + fmt.Printf("执行下一节点-34324234-r.Step:%v\n----->r.NextStep:%v\n", r.Step, r.NextStep) + currentStep, nextStep = r.GainRunNodeNew(userKey, nextStep, AgreeToRefuse, "自动审批") + } else { + //判断是否向下一个节点的人发送信息 + r.SendWecharMsgTopct(userKey, nextStep) + } + r.Step = currentStep + r.NextStep = nextStep + return + case 2: + r.SendWecharMsgTopct("", r.NextStep) + if r.NextStep > 0 { + currentStep, nextStep = r.GainRunNodeNew(userKey, nextStep, AgreeToRefuse, "自动审批") + } + r.Step = currentStep + r.NextStep = nextStep + return + default: + } + } + } + } + return +} + +/* +* +@ 作者: 秦东 +@ 时间: 2024-12-14 13:37:04 +@ 功能: 获取下一节点审批人 +@ 参数 + + # + +@ 返回值 + + #userKeyAry 节点审批人 + +@ 方法原型 + + # +*/ +func (r *RunWorkFlow) NextNodePeople() (userKeyAry []string) { + if r.NextStep > 0 { + for _, v := range r.FlowList { + if r.NextStep == v.Step { + for _, ov := range v.Operator { + if ov.Id != "" { + userKeyAry = append(userKeyAry, ov.Id) + } + } + } + } + } + return +} + +/* +* +@ 作者: 秦东 +@ 时间: 2024-12-13 13:38:52 +@ 功能: 向下一个节点人员发送消息 +@ 参数 + + # + +@ 返回值 + + # + +@ 方法原型 + + # +*/ +func (r *RunWorkFlow) SendWecharMsgTopct(userKey string, nextStep int) { + fmt.Printf("发射消息---->userKey: %v\n nextStep: %v\n", userKey, nextStep) + if nextStep > 0 { + var sendUserAry []string + nodeTitle := "" + for _, v := range r.FlowList { + if nextStep == v.Step { + nodeTitle = v.NodeName + if v.ExamineMode != 1 { + for _, ov := range v.Operator { + if userKey != "" { + if ov.Id != userKey && ov.Wechat != "" { + sendUserAry = append(sendUserAry, ov.Wechat) + } + } else { + if ov.Wechat != "" { + sendUserAry = append(sendUserAry, ov.Wechat) + } + } + + } + } else { + uuidStr := strconv.FormatInt(r.Uuid, 10) + for _, ov := range v.Operator { + if len(ov.LogList) > 0 { + for _, lv := range ov.LogList { + if lv.UID != uuidStr { + if userKey != "" { + if ov.Id != userKey && ov.Wechat != "" { + sendUserAry = append(sendUserAry, ov.Wechat) + } + } else { + if ov.Wechat != "" { + sendUserAry = append(sendUserAry, ov.Wechat) + } + } + } + } + } else { + if ov.Wechat != "" { + sendUserAry = append(sendUserAry, ov.Wechat) + } + } + + } + } + + } + } + fmt.Printf("发射消息--2-->sendUserAry: %v\n nextStep: %v\n", sendUserAry, nextStep) + if len(sendUserAry) > 0 { + taskId := publicmethod.GetUUid(1) + tableName, qouteTitle, contList := r.GainTitleAndCreate() + var sendMsg workWechat.SendMessage + sendMsg.Touser = strings.Join(sendUserAry, "|") + sendMsg.TemplateCard.Source.Desc = nodeTitle + sendMsg.TemplateCard.MainTitle.Title = tableName + sendMsg.TemplateCard.TaskId = strconv.FormatInt(taskId, 10) + sendMsg.TemplateCard.QuoteArea.Quote_text = qouteTitle + sendMsg.TemplateCard.HorizontalContentList = append(sendMsg.TemplateCard.HorizontalContentList, contList...) + var jumpInfo workWechat.Jump_list + jumpInfo.Title = "前往处理" + jumpInfo.Url = "wap.gyhlw.group" + jumpInfo.Type = 1 + sendMsg.TemplateCard.JumpList = append(sendMsg.TemplateCard.JumpList, jumpInfo) + sendMsg.TemplateCard.CardAction.Type = 1 + sendMsg.TemplateCard.CardAction.Url = "wap.gyhlw.group" + sendMsg.SendMsg("stzl", 1) + } + } +} + +/* +* +@ 作者: 秦东 +@ 时间: 2024-12-13 15:38:55 +@ 功能: 获取指定标题和申请人相关信息 + +#tableName 一级标题 +#qouteTitle 引用文件 +#peopleInfoAry 发起人 +*/ +func (r *RunWorkFlow) GainTitleAndCreate() (tableName, qouteTitle string, peopleInfoAry []workWechat.Horizontal_content_list) { + var tableInFor modelAppPlatform.CustomerForm + tableInFor.GetCont(map[string]interface{}{"`signCode`": r.SendWecharMsg.TableKey}, "`name`", "`tablename`", "`listjson`") + tableName = tableInFor.Name + qouteTitle = tableInFor.Name + var ceraterInfo modelshr.PersonArchives + ceraterInfo.GetCont(map[string]interface{}{"`key`": r.SendWecharMsg.Creater}, "`name`", "`number`", "`wechat`", "`work_wechat`") + var peopleInfo workWechat.Horizontal_content_list + peopleInfo.Keyname = "申请人" + peopleInfo.Value = fmt.Sprintf("%v(%v)", ceraterInfo.Name, ceraterInfo.Number) + if ceraterInfo.WorkWechat != "" { + peopleInfo.Type = 3 + peopleInfo.UserId = ceraterInfo.WorkWechat + } else if ceraterInfo.Wechat != "" { + peopleInfo.Type = 3 + peopleInfo.UserId = ceraterInfo.Wechat + } + peopleInfoAry = append(peopleInfoAry, peopleInfo) + if tableInFor.ListJson != "" { + var listInfo customerform.ListPageFields + json.Unmarshal([]byte(tableInFor.ListJson), &listInfo) + if listView, isOk := listInfo.View["list"]; isOk { + if len(listView.Form.Title) > 0 { + tableForm := publicmethod.MapOut[string]() + overall.CONSTANT_DB_CustomerForm.Table(tableInFor.TableNames).Where("`masters_key` = ?", r.SendWecharMsg.MastersKey).Find(&tableForm) + var qouteTitleAry []string + // fmt.Printf("list表单:%v\n\n\n%v\n\n\n", listView.Form.Title, tableForm) + for _, v := range listView.Form.Title { + if formVal, isOk := tableForm[v]; isOk { + // fmt.Printf("list表单-----v----->:%v\n\n\n%v\n\n\n", formVal, publicmethod.TypeToInterface(formVal)) + qouteTitleAry = append(qouteTitleAry, publicmethod.TypeToInterface(formVal)) + } + } + if len(qouteTitleAry) > 0 { + qouteTitle = strings.Join(qouteTitleAry, "-") + } + } + } else if dateView, isOk := listInfo.View["date"]; isOk { + if len(dateView.Form.Title) > 0 { + tableForm := publicmethod.MapOut[string]() + overall.CONSTANT_DB_CustomerForm.Table(tableInFor.TableNames).Where("`masters_key` = ?", r.SendWecharMsg.MastersKey).Find(&tableForm) + var qouteTitleAry []string + // fmt.Printf("date表单:%v\n\n\n%v\n\n\n", dateView.Form.Title, tableForm) + for _, v := range dateView.Form.Title { + if formVal, isOk := tableForm[v]; isOk { + qouteTitleAry = append(qouteTitleAry, publicmethod.TypeToInterface(formVal)) + } + } + if len(qouteTitleAry) > 0 { + qouteTitle = strings.Join(qouteTitleAry, "-") + } + } + } else if timeView, isOk := listInfo.View["time"]; isOk { + if len(timeView.Form.Title) > 0 { + tableForm := publicmethod.MapOut[string]() + overall.CONSTANT_DB_CustomerForm.Table(tableInFor.TableNames).Where("`masters_key` = ?", r.SendWecharMsg.MastersKey).Find(&tableForm) + var qouteTitleAry []string + // fmt.Printf("time表单:%v\n\n\n%v\n\n\n", timeView.Form.Title, tableForm) + for _, v := range timeView.Form.Title { + if formVal, isOk := tableForm[v]; isOk { + qouteTitleAry = append(qouteTitleAry, publicmethod.TypeToInterface(formVal)) + } + } + if len(qouteTitleAry) > 0 { + qouteTitle = strings.Join(qouteTitleAry, "-") + } + } + } else if ganttView, isOk := listInfo.View["gantt"]; isOk { + if len(ganttView.Form.Title) > 0 { + tableForm := publicmethod.MapOut[string]() + overall.CONSTANT_DB_CustomerForm.Table(tableInFor.TableNames).Where("`masters_key` = ?", r.SendWecharMsg.MastersKey).Find(&tableForm) + var qouteTitleAry []string + // fmt.Printf("gantt表单:%v\n\n\n%v\n\n\n", ganttView.Form.Title, tableForm) + for _, v := range ganttView.Form.Title { + if formVal, isOk := tableForm[v]; isOk { + qouteTitleAry = append(qouteTitleAry, publicmethod.TypeToInterface(formVal)) + } + } + if len(qouteTitleAry) > 0 { + qouteTitle = strings.Join(qouteTitleAry, "-") + } + } + } else if mapView, isOk := listInfo.View["map"]; isOk { + if len(mapView.Form.Title) > 0 { + tableForm := publicmethod.MapOut[string]() + overall.CONSTANT_DB_CustomerForm.Table(tableInFor.TableNames).Where("`masters_key` = ?", r.SendWecharMsg.MastersKey).Find(&tableForm) + var qouteTitleAry []string + // fmt.Printf("map表单:%v\n\n\n%v\n\n\n", mapView.Form.Title, tableForm) + for _, v := range mapView.Form.Title { + if formVal, isOk := tableForm[v]; isOk { + qouteTitleAry = append(qouteTitleAry, publicmethod.TypeToInterface(formVal)) + } + } + if len(qouteTitleAry) > 0 { + qouteTitle = strings.Join(qouteTitleAry, "-") + } + } + } else if cardView, isOk := listInfo.View["card"]; isOk { + if len(cardView.Form.Title) > 0 { + tableForm := publicmethod.MapOut[string]() + overall.CONSTANT_DB_CustomerForm.Table(tableInFor.TableNames).Where("`masters_key` = ?", r.SendWecharMsg.MastersKey).Find(&tableForm) + var qouteTitleAry []string + // fmt.Printf("card表单:%v\n\n\n%v\n\n\n", cardView.Form.Title, tableForm) + for _, v := range cardView.Form.Title { + if formVal, isOk := tableForm[v]; isOk { + qouteTitleAry = append(qouteTitleAry, publicmethod.TypeToInterface(formVal)) + } + } + if len(qouteTitleAry) > 0 { + qouteTitle = strings.Join(qouteTitleAry, "-") + } + } + } + } + // fmt.Printf("qouteTitle表单:%v\n\n\n", qouteTitle) + return +} + +/* +* +@ 作者: 秦东 +@ 时间: 2024-12-13 11:26:32 +@ 功能: 判断下一个节点是否继续执行 +@ 参数 + + #userKey 当前执行人 + #nextStep 下一个节点 + +@ 返回值 + + # + +@ 方法原型 + + # +*/ +func (r *RunWorkFlow) ToNextNodeRunOrClose(userKey string, nextStep int) (isRun bool) { + isRun = false + if nextStep == 0 { + return isRun + } + runIdStr := strconv.FormatInt(r.RunUid, 10) + fmt.Printf("判断下一个节点是否继续执行:userKey:%v------->nextStep:%v\n", userKey, nextStep) + for _, v := range r.FlowList { + if nextStep == v.Step { + fmt.Printf("判断下一个节点是否继续执行:v.Step:%v----1--->v.Types:%v\n", v.Step, v.Types) + switch v.Types { + case 1, 3: + isMyTrue := false + optUserCount := len(v.Operator) + allPick := 0 + for _, ov := range v.Operator { + if ov.Id == userKey { + isMyTrue = true + } + for _, ol := range ov.LogList { + if runIdStr == ol.UID { + allPick++ + } + } + } + if isMyTrue { + if optUserCount > 1 { + switch v.ExamineMode { + case 1, 2: + if allPick == 0 { + isRun = true + } else { + if allPick == optUserCount { + isRun = true + } else { + isRun = false + } + } + + default: + isRun = true + } + } else { + if optUserCount == 1 { + isRun = true + } else { + isRun = false + } + } + // isRun = true + } + fmt.Printf("判断下一个节点是否继续执行:isMyTrue:%v----3--->isRun:%v\n----3--->optUserCount:%v\n----3--->v.ExamineMode:%v\n", isMyTrue, isRun, optUserCount, v.ExamineMode) + case 2: + isRun = true + default: + isRun = false + } + } + } + return isRun +} + +/* +* +@ 作者: 秦东 +@ 时间: 2024-12-17 08:42:53 +@ 功能: 判断是否执行下一步(副本) +@ 参数 + + #runSetup 当前要执行的步骤 + #nodeInfo 当前节点状态 + +@ 返回值 + + #currentStep 计算后的当前节点 + #nextStep 计算后的下一个节点 + +@ 方法原型 + + # +*/ +func (r *RunWorkFlow) TallyPaceStepes(userKey string, runSetup int, nodeInfo RunFlow) (currentStep, nextStep int) { + runIdStr := strconv.FormatInt(r.RunUid, 10) + nodeInfoJson, _ := json.Marshal(nodeInfo) + fmt.Printf("计步器runIdStr:%v---1-----runSetup:%v--------nodeInfo.Types:%v----------->nodeInfoJson:%v\n", runIdStr, runSetup, nodeInfo.Types, string(nodeInfoJson)) + if nodeInfo.Types == 2 { + currentStep = runSetup + nextStep = runSetup + 1 + return + } else { + + optUserCount := len(nodeInfo.Operator) //此节点有几个人审批 + jiBuQi := 0 + for _, v := range nodeInfo.Operator { + for _, l := range v.LogList { + if l.UID == runIdStr { + jiBuQi++ + } + } + // if v.Id == userKey { + // jiBuQi++ + // } + } + fmt.Printf("计步器 optUserCount:%v---2-----runSetup:%v-----jiBuQi:%v\n", optUserCount, runSetup, jiBuQi) + if jiBuQi > 0 { //存在有人审批的情况 + if jiBuQi >= optUserCount { //当前节点所有人已经审批则进入下一个节点 + currentStep = runSetup + if currentStep >= r.TotalSteps { + currentStep = r.TotalSteps + nextStep = 0 + } else { + nextStep = currentStep + 1 + if nextStep >= r.TotalSteps { + nextStep = r.TotalSteps + } + } + } else { //有未参与审批的人员,根据节点属性进行判断 + fmt.Printf("计步器 nodeInfo.ExamineMode :%v---3-----runSetup:%v-----r.TotalSteps:%v\n", nodeInfo.ExamineMode, runSetup, r.TotalSteps) + switch nodeInfo.ExamineMode { //多人审批时采用的审批方式。0:无操作 1依次审批 2会签 3:或签 + case 1, 2: + if runSetup >= r.TotalSteps { //当前步数大于等于总步数时 + currentStep = r.TotalSteps + nextStep = 0 + } else { + currentStep = runSetup - 1 + if currentStep <= 0 { + currentStep = 1 + } + if currentStep >= r.TotalSteps { + currentStep = r.TotalSteps + nextStep = 0 + } else { + nextStep = currentStep + 1 + if nextStep >= r.TotalSteps { + nextStep = r.TotalSteps + } + } + } + case 3: + if runSetup >= r.TotalSteps { //当前步数大于等于总步数时 + currentStep = r.TotalSteps + nextStep = 0 + } else { + currentStep = runSetup + if currentStep >= r.TotalSteps { + currentStep = r.TotalSteps + nextStep = 0 + } else { + nextStep = currentStep + 1 + if nextStep >= r.TotalSteps { + nextStep = r.TotalSteps + } + } + } + default: + } + } + + } else { //此节点没有人审批 + fmt.Printf("计步器 r.TotalSteps:%v---4-----runSetup:%v-----jiBuQi:%v\n", r.TotalSteps, runSetup, jiBuQi) + if optUserCount > 1 { + currentStep = runSetup - 1 + if currentStep >= r.TotalSteps { + currentStep = r.TotalSteps + nextStep = 0 + } else { + nextStep = currentStep + 1 + if nextStep >= r.TotalSteps { + nextStep = r.TotalSteps + } + } + } else { + currentStep = runSetup + if currentStep >= r.TotalSteps { + currentStep = r.TotalSteps + nextStep = 0 + } else { + nextStep = currentStep + 1 + if nextStep >= r.TotalSteps { + nextStep = r.TotalSteps + } + } + } + + } + } + return +} + +/* +* +@ 作者: 秦东 +@ 时间: 2024-12-13 10:27:15 +@ 功能: 判断是否执行下一步 +#nodeInfo 当前执行节点内容 +*/ +func (r *RunWorkFlow) TallyPaceStep(runSetup int, nodeInfo RunFlow) (currentStep, nextStep int) { + nodeInfoJson, _ := json.Marshal(nodeInfo) + fmt.Printf("判断是否执行下一步runSetup:%v=============>r.TotalSteps:%v=============>nodeInfo.ExamineMode:%v=============>%v\n", runSetup, r.TotalSteps, nodeInfo.ExamineMode, string(nodeInfoJson)) + switch nodeInfo.ExamineMode { //多人审批时采用的审批方式。0:无操作 1依次审批 2会签 3:或签 + case 1, 2: + runIdStr := strconv.FormatInt(r.RunUid, 10) + optUserCount := len(nodeInfo.Operator) + jiBuQi := 0 + for _, v := range nodeInfo.Operator { + for _, l := range v.LogList { + if l.UID == runIdStr { + jiBuQi++ + } + } + } + fmt.Printf("计步器runIdStr:%v---1-----currentStep:%v--------optUserCount:%v----------->jiBuQi:%v\n", runIdStr, currentStep, optUserCount, jiBuQi) + if jiBuQi >= optUserCount { //会签人员全部签署 + if runSetup >= r.TotalSteps { //当前步数大于等于总步数时 + currentStep = r.TotalSteps + nextStep = 0 + } else { + currentStep = runSetup + if currentStep >= r.TotalSteps { + currentStep = r.TotalSteps + nextStep = 0 + } else { + nextStep = currentStep + 1 + if nextStep >= r.TotalSteps { + nextStep = r.TotalSteps + } + } + } + } else { //会签中有人未签署 + if runSetup >= r.TotalSteps { //当前步数大于等于总步数时 + currentStep = r.TotalSteps + nextStep = 0 + } else { + currentStep = runSetup + if currentStep >= r.TotalSteps { + currentStep = r.TotalSteps + nextStep = 0 + } else { + nextStep = currentStep + 1 + if nextStep >= r.TotalSteps { + nextStep = r.TotalSteps + } + } + } + } + case 3: + + runIdStr := strconv.FormatInt(r.RunUid, 10) + jiBuQi := 0 + for _, v := range nodeInfo.Operator { + for _, l := range v.LogList { + if l.UID == runIdStr { + jiBuQi++ + } + } + } + + fmt.Printf("计步器runIdStr:%v----2----currentStep:%v--------runSetup:%v----------->jiBuQi:%v\n", runIdStr, currentStep, runSetup, jiBuQi) + + if jiBuQi > 0 { //或签中已有一人签署 + if runSetup >= r.TotalSteps { //当前步数大于等于总步数时 + currentStep = r.TotalSteps + nextStep = 0 + } else { + currentStep = runSetup + if currentStep >= r.TotalSteps { + currentStep = r.TotalSteps + nextStep = 0 + } else { + nextStep = currentStep + 1 + if nextStep >= r.TotalSteps { + nextStep = r.TotalSteps + } + } + } + } else { //没有任何人签署 + if runSetup >= r.TotalSteps { //当前步数大于等于总步数时 + currentStep = r.TotalSteps + nextStep = 0 + } else { + currentStep = runSetup + if currentStep >= r.TotalSteps { + currentStep = r.TotalSteps + nextStep = 0 + } else { + nextStep = currentStep + 1 + if nextStep >= r.TotalSteps { + nextStep = r.TotalSteps + } + } + } + } + default: + + if runSetup >= r.TotalSteps { //当前步数大于等于总步数时 + currentStep = r.TotalSteps + nextStep = 0 + } else { + currentStep = runSetup + if currentStep >= r.TotalSteps { + currentStep = r.TotalSteps + nextStep = 0 + } else { + nextStep = currentStep + 1 + if nextStep >= r.TotalSteps { + nextStep = r.TotalSteps + } + } + } + fmt.Printf("计步器r.TotalSteps:%v----3----currentStep:%v--------runSetup:%v----------->jiBuQi:%v\n", r.TotalSteps, currentStep, runSetup, currentStep) + } + return +} + +/* +* +@ 作者: 秦东 +@ 时间: 2024-12-13 08:20:36 +@ 功能: 处理执行节点参与人和执行人操作记录 +@ 参数 + + #userKey 操作人 + #runUid 执行Uid + #AgreeToRefuse 同意或者驳回 + #operator 当前节点操作人 + #suggest 审批意见 + +@ 返回值 + + #OperatorAry 节点操作记录 + #nodeUser 节点参与人 + +@ 方法原型 + + # +*/ +func CopeParticipantEnforcerLog(userKey string, runUid int64, AgreeToRefuse int, operator []OperatorList, suggest string) (OperatorAry []OperatorList, nodeUser []string) { + var logInfo LogList + logInfo.State = AgreeToRefuse //状态 1、未操作;2、通过;3、驳回 + logInfo.TimeVal = publicmethod.UnixTimeToDay(time.Now().Unix(), 1) //操作时间 + logInfo.UID = strconv.FormatInt(runUid, 10) + logInfo.Cause = suggest + isNewAdd := true //判断当前执行人是否再执行列表中 + for _, v := range operator { + nodeUser = append(nodeUser, v.Id) + if v.Id == userKey { + v.LogList = append(v.LogList, logInfo) + isNewAdd = false + } + OperatorAry = append(OperatorAry, v) + } + if isNewAdd { //如果此人没有在此节点中,则增加此人 + var myInfo modelshr.ManCont + myInfo.GetCont(map[string]interface{}{"`key`": userKey}) + userKey := strconv.FormatInt(myInfo.Key, 10) + nodeUser = append(nodeUser, userKey) + nodeMan := TransformPublicUs(myInfo) + nodeMan.LogList = append(nodeMan.LogList, logInfo) + OperatorAry = append(OperatorAry, nodeMan) + } + return +} + +/* +* +@ 作者: 秦东 +@ 时间: 2023-11-15 11:37:00 +@ 功能: 装通用执行人数据 +@ 参数 + + #v 人员信息 + +@ 返回值 + + #userCont 统一操作人信息 + +@ 方法原型 + + # +*/ +func TransformPublicUs(v modelshr.ManCont) (userCont OperatorList) { + userCont.Id = strconv.FormatInt(v.Key, 10) + userCont.Types = 1 //1:人员;2:角色;3:行政组织 + userCont.Name = v.Name //操作人姓名 + userCont.Number = v.Number //操作人工号 + userCont.Icon = v.Icon //操作人头像 + userCont.IconBase64 = v.IconPhpto //操作人头像 + userCont.Wechat = v.Wechat //微信Openid、 + userCont.Tel = v.Mobilephone + if v.WorkWechat != "" { + userCont.Wechat = v.WorkWechat //微信Openid + } + if v.Company != 0 { + var orgGroupCont modelshr.AdministrativeOrganization + orgGroupCont.GetCont(map[string]interface{}{"`id`": v.Company}, "`name`") + userCont.CompanyName = orgGroupCont.Name //公司名称 + } + _, companyId, _, _, _ := publicmethod.GetOrgStructurees(v.AdminOrg) + userCont.DepartmentId = companyId //分厂Id + if companyId != 0 { + var orgCont modelshr.AdministrativeOrganization + orgCont.GetCont(map[string]interface{}{"`id`": companyId}, "`name`") + userCont.DepartmentName = orgCont.Name //分厂名称 + } + //获取岗位 + if v.Position != 0 { + var postCont modelshr.Position + postCont.GetCont(map[string]interface{}{"`id`": v.Position}, "`name`") + userCont.PostId = v.Position //职务Id + userCont.PostName = postCont.Name //职务名称 + } + if v.TeamId != 0 { + var teamCont modelshr.TeamGroup + teamCont.GetCont(map[string]interface{}{"`id`": v.TeamId}, "`name`") + userCont.Tema = v.TeamId //班组Id + userCont.TemaName = teamCont.Name //班组名称 + } + return +} + +/* +* +@ 作者: 秦东 +@ 时间: 2024-12-17 11:04:48 +@ 功能: 执行工作流节点处理 +@ 参数 + + #userKey 当前处理人 + #AgreeToRefuse 1:同意,2:驳回 + #Suggest 审批意见 + +@ 返回值 + + # + +@ 方法原型 + + # +*/ +func (r *RunWorkFlow) RunNodeHandle(userKey int64, AgreeToRefuse int, Suggest string) { + fmt.Printf("执行工作流节点处理------------->userKey=======>%v, AgreeToRefuse=======>%v, Suggest=======>%v, r.Step=======>%v\n", userKey, AgreeToRefuse, Suggest, r.Step) + //判断流程是否存在 + if len(r.FlowList) < 1 { + return + } + userKeyStr := strconv.FormatInt(userKey, 10) //当前操作人识别符转字符类型 + for i, v := range r.FlowList { //遍历流程步骤判断当前执行的哪一步 + if v.Step == r.Step { //获取当前节点数据 + switch v.Types { //0:发起节点;1:审批节点;2:抄送;3:执行节点 + case 1, 3: + //判断操作人是有操作权限或是否已经操作过 + r.IsRun, r.Msg = JudgeOperUser(userKey, r.RunUid, v.Operator) //判断操作人是有操作权限或是否已经操作过 + if r.IsRun { + return + } else { + fmt.Printf("执行工作流节点处理-------1------>userKey=======>%v\n AgreeToRefuse=======>%v\n Suggest=======>%v\n\n", userKey, AgreeToRefuse, Suggest) + if AgreeToRefuse != 1 { //驳回操作 + v.Status = 3 //驳回 + operatorAry, nodeUser := CopeParticipantEnforcerLog(userKeyStr, r.RunUid, 3, v.Operator, Suggest) + r.FlowList[i].Operator = operatorAry + r.Participant = append(r.Participant, nodeUser...) + r.Step, r.NextStep, _ = r.CallBackToNode(v.GoBackNode) + fmt.Printf("执行工作流节点处理--------2----->userKey=======>%v\n AgreeToRefuse=======>%v\n Suggest=======>%v\n operatorAry=======>%v\n nodeUser=======>%v\n GoBackNode=======>%v\n r=======>%v\n\n", userKey, AgreeToRefuse, Suggest, operatorAry, nodeUser, v.GoBackNode, r) + r.SendWecharMsgTextCard(r.Step, "驳回", Suggest) + } else { //同意操作 + v.Status = 2 //同意 + operatorAry, nodeUser := CopeParticipantEnforcerLog(userKeyStr, r.RunUid, 2, v.Operator, Suggest) + r.FlowList[i].Operator = operatorAry + r.Participant = append(r.Participant, nodeUser...) + currentStep, nextStep := r.TallyPaceStepes(userKeyStr, r.Step, r.FlowList[i]) //获取执行步伐 + fmt.Printf("执行工作流节点处理-------3------>userKey=======>%v\n AgreeToRefuse=======>%v\n Suggest=======>%v\n currentStep=======>%v\n nextStep=======>%v\n\n", userKey, AgreeToRefuse, Suggest, currentStep, nextStep) + if nextStep > 0 { + if r.ToNextNodeRunOrClose(userKeyStr, nextStep) { + r.Step = nextStep + // r.NextStep = nextStep + r.RunNodeHandle(userKey, AgreeToRefuse, Suggest) + } else { + userKeyStr := strconv.FormatInt(userKey, 10) + //判断是否向下一个节点的人发送信息 + r.SendWecharMsgTopct(userKeyStr, nextStep) + } + } + r.Step = currentStep + r.NextStep = nextStep + taskmanagement.FlowRunLog(r.Uuid, userKey, AgreeToRefuse, v.NodeKey, Suggest, "") + } + } + return + case 2: //抄送 + var sendUserAry []string + for _, ov := range v.Operator { + if !publicmethod.IsInTrue[string](ov.Id, r.MakeCopy) { + r.MakeCopy = append(r.MakeCopy, ov.Id) + } + if ov.Wechat != "" { + if !publicmethod.IsInTrue[string](ov.Wechat, sendUserAry) { + sendUserAry = append(sendUserAry, ov.Wechat) + } + } + userkIntId, _ := strconv.ParseInt(ov.Id, 10, 64) + title := fmt.Sprintf("向%v发送抄送数据", ov.Name) + taskmanagement.FlowRunLog(r.Uuid, userkIntId, AgreeToRefuse, v.NodeKey, title, "") + } + r.SendWecharMsgTopct(userKeyStr, r.Step) + currentStep, nextStep := r.TallyPaceStepes(userKeyStr, r.Step, r.FlowList[i]) + if nextStep > 0 { + if r.ToNextNodeRunOrClose(userKeyStr, nextStep) { + r.Step = currentStep + r.NextStep = nextStep + r.RunNodeHandle(userKey, AgreeToRefuse, Suggest) + } + } + r.Step = currentStep + r.NextStep = nextStep + return + default: + v.Status = 2 //同意 + operatorAry, nodeUser := CopeParticipantEnforcerLog(userKeyStr, r.RunUid, 2, v.Operator, "发起流程") + r.FlowList[i].Operator = operatorAry + r.Participant = append(r.Participant, nodeUser...) + currentStep, nextStep := r.TallyPaceStepes(userKeyStr, r.Step, r.FlowList[i]) //获取执行步伐 + fmt.Printf("执行工作流节点处理-------4------>userKey=======>%v\n AgreeToRefuse=======>%v\n Suggest=======>%v\n currentStep=======>%v\n nextStep=======>%v\n\n", userKey, AgreeToRefuse, Suggest, currentStep, nextStep) + if nextStep > 0 { + if r.ToNextNodeRunOrClose(userKeyStr, nextStep) { + r.Step = currentStep + r.NextStep = nextStep + r.RunNodeHandle(userKey, AgreeToRefuse, "发起流程") + } else { + userKeyStr := strconv.FormatInt(userKey, 10) + //判断是否向下一个节点的人发送信息 + r.SendWecharMsgTopct(userKeyStr, nextStep) + } + } + r.Step = currentStep + r.NextStep = nextStep + taskmanagement.FlowRunLog(r.Uuid, userKey, AgreeToRefuse, v.NodeKey, "发起流程", "") + } + } + } +} + +/* +* +@ 作者: 秦东 +@ 时间: 2024-12-19 08:34:32 +@ 功能: 驳回到指定节点 +@ 参数 + + #nodeKey 返回的节点 + +@ 返回值 + + # + +@ 方法原型 + + # +*/ +func (r *RunWorkFlow) CallBackToNode(nodeKey string) (creeStep, nextStep int, isNew bool) { + if nodeKey == "beginnode" { + creeStep = 1 + if len(r.FlowList) >= 2 { + nextStep = 2 + isNew = false + } else { + nextStep = 0 + isNew = true + } + r.Step = creeStep + r.NextStep = nextStep + } else { + creeStep = 1 + nextStep = 0 + for _, v := range r.FlowList { + if nodeKey == v.NodeKey { + creeStep = v.Step + nextStep := v.Step + 1 + if nextStep <= len(r.FlowList) { + isNew = false + } else { + nextStep = 0 + isNew = true + } + } + } + r.Step = creeStep + r.NextStep = nextStep + } + return +} + +/* +* +@ 作者: 秦东 +@ 时间: 2024-12-17 11:45:12 +@ 功能: 判断操作人是否已经操作过或具备当前操作权限,并写入操作记录 +@ 参数 + + #userKey 当前操作人 + #runId 执行码 + #ExamineMode 多人审批时采用的审批方式。0:无操作 1依次审批 2会签 3:非会签 + #Operator 操作人 + +@ 返回值 + + #状态 + #说明 + +@ 方法原型 + + # +*/ +func JudgeOperUser(userKey, runId int64, Operator []OperatorList) (bool, string) { + if len(Operator) < 1 { + return true, "您不是此节点操作人!请不要提交!" + } + jsonval, _ := json.Marshal(Operator) + fmt.Printf("判断操作人是否已经操作过userKey:%v\n\n\n\n runId:%v\n\n\n\njsonval:%v\n\n\n\n", userKey, runId, string(jsonval)) + caoZuoQuanXian := true + for _, v := range Operator { + if v.Id == strconv.FormatInt(userKey, 10) { + caoZuoQuanXian = false + if len(v.LogList) > 0 { + for _, m := range v.LogList { + if m.UID == strconv.FormatInt(runId, 10) { + return true, "您已经操作过此节点!请不要重复提交!" + } + } + } + } + } + if caoZuoQuanXian { + return true, "您不是此节点操作人!请不要提交!" + } + return false, "" +} + +/* +* +@ 作者: 秦东 +@ 时间: 2024-12-17 15:53:24 +@ 功能: 发送文本卡片消息 +@ 参数 + + #userKey 接收人 + +@ 返回值 + + # + +@ 方法原型 + + # +*/ +func (r *RunWorkFlow) SendWecharMsgTextCard(runStep int, nodeTitle, content string) { + if runStep == 0 { + runStep = 1 + } + if runStep > len(r.FlowList) { + runStep = len(r.FlowList) + } + var sendUser []string + for _, v := range r.FlowList { + if v.Step == runStep { + for _, lv := range v.Operator { + if lv.Wechat != "" && !publicmethod.IsInTrue[string](lv.Wechat, sendUser) { + sendUser = append(sendUser, lv.Wechat) + } + } + } + } + // fmt.Printf("驳回数据接收人:%v", sendUser) + if len(sendUser) > 0 { + tableName, qouteTitle, _ := r.GainTitleAndCreate() + var sendMsg workWechat.SendMessage + sendMsg.Touser = strings.Join(sendUser, "|") + sendMsg.Msgtype = "textcard" + sendMsg.Textcard.Title = nodeTitle + sendMsg.Textcard.Description = fmt.Sprintf("
%v
%v
%v
驳回原因:%v
", publicmethod.UnixTimeToDay(time.Now().Unix(), 11), tableName, qouteTitle, content) + sendMsg.Textcard.Url = "wap.gyhlw.group" + sendMsg.Textcard.Btntxt = "前往查看" + fmt.Printf("驳回数据接收人2:%v", sendMsg) + sendMsg.SendMsg("stzl", 1) + } +} diff --git a/api/version1/workFlow/flow.go b/api/version1/workFlow/flow.go new file mode 100644 index 0000000..95de223 --- /dev/null +++ b/api/version1/workFlow/flow.go @@ -0,0 +1,341 @@ +package workflow + +import ( + "appPlatform/api/version1/taskplatform/taskmanagement" + "appPlatform/models/customerForm" + "appPlatform/models/modelAppPlatform" + "appPlatform/models/modelshr" + "appPlatform/overall" + "appPlatform/overall/publicmethod" + "encoding/json" + "fmt" + "strconv" + "strings" + "time" + + "github.com/gin-gonic/gin" +) + +/* +* +@ 作者: 秦东 +@ 时间: 2024-12-12 14:37:41 +@ 功能: 启动流程 +@ 参数 + + # + +@ 返回值 + + # + +@ 方法原型 + + # +*/ +func (a *ApiMethod) StartProcess(c *gin.Context) { + var requestData StartWorkFlow + err := c.ShouldBindJSON(&requestData) + if err != nil { + publicmethod.Result(10001, err, c, "未知进程!不可执行") + return + } + if requestData.Id == "" { + publicmethod.Result(10001, err, c, "未知进程!不可执行") + return + } + if requestData.State == 0 { + requestData.State = 3 + } + + var taskInfo customerForm.TaskRecord + taskInfo.GetCont(map[string]interface{}{"`masters_key`": requestData.Id}, "`title`", "`creater`", `masters_key`, "`version_id`", "`flow_key`", "`flow_run_sing`", "`tableKey`", "`appKey`", "`runFlowId`") + if len(requestData.FlowList) < 1 { + taskInfo.EiteCont(map[string]interface{}{"`masters_key`": requestData.Id}, map[string]interface{}{"`status`": 1}) + if taskInfo.RunFlowId != 0 { + var runFlowInfo customerForm.RunWorkflow + runFlowInfo.EiteCont(map[string]interface{}{"`id`": taskInfo.RunFlowId}, map[string]interface{}{"`status`": 1}) + } + publicmethod.Result(10001, err, c, "未知进程!不可执行") + return + } + //获取当前操作参数 + context, _ := c.Get(overall.MyContJwt) + var userCont modelshr.ManCont + userCont.GetLoginCont(context) //当前操作人 + startCreaterKey := strconv.FormatInt(userCont.Key, 10) //当前操作人 + creetTime := time.Now().Unix() //当前时间 + + //获取流程详情 + var flowVersionInfo modelAppPlatform.WorkFlowVersion + flowVersionInfo.GetCont(map[string]interface{}{"`id`": taskInfo.FlowRunSing}, "`content`") + + uuid := publicmethod.GetUUid(1) + runUuId := publicmethod.GetUUid(6) + //工作流执行主体 + var workFlowInfo customerForm.RunWorkflow + workFlowInfo.Id = uuid + workFlowInfo.FlowKey = taskInfo.FlowKey //统一识别符(任务标识符) + workFlowInfo.Version = strconv.FormatInt(taskInfo.FlowRunSing, 10) //工作流版本 + workFlowInfo.VersionCont = flowVersionInfo.Content //当前工作流内容 + workFlowInfo.Creater = userCont.Key //流程发起人 + workFlowInfo.Status = requestData.State //状态:1、草稿;2:驳回;3:审批中;4:归档;5:删除 + workFlowInfo.StartTime = creetTime //开始时间 + workFlowInfo.UpdateTime = creetTime //更新时间 + workFlowInfo.RunKey = runUuId //当前执行识别符 + + //流程结构体 + var executeWorkflow RunWorkFlow + executeWorkflow.Step = 1 //执行第1部 发起流程。当前为第一步 + executeWorkflow.FlowList = requestData.FlowList //工作流主体 + executeWorkflow.Participant = append(executeWorkflow.Participant, startCreaterKey) //当前操作人 + executeWorkflow.TotalSteps = len(requestData.FlowList) //流程总长度 + executeWorkflow.Uuid = uuid //流程唯一识别符 + executeWorkflow.RunUid = runUuId //执行Uid + + executeWorkflow.SendWecharMsg.AppKey = taskInfo.AppKey + executeWorkflow.SendWecharMsg.Creater = taskInfo.Creater + executeWorkflow.SendWecharMsg.MastersKey = taskInfo.MastersKey + executeWorkflow.SendWecharMsg.TableKey = taskInfo.TableKey + executeWorkflow.SendWecharMsg.RunFlowId = taskInfo.RunFlowId + if requestData.State == 3 { + workFlowInfo.CurrentStep, workFlowInfo.NextStep = executeWorkflow.GainRunNodeNew(startCreaterKey, 1, 2, "发起审批") + } else { + workFlowInfo.CurrentStep, workFlowInfo.NextStep = executeWorkflow.GainRunNodeNew(startCreaterKey, 1, 1, "") + } + fmt.Printf("获取得步骤:workFlowInfo.CurrentStep :%v\n workFlowInfo.NextStep :%v\n", workFlowInfo.CurrentStep, workFlowInfo.NextStep) + flowJsonCont, _ := json.Marshal(executeWorkflow.FlowList) //将步进流转化成json流 + workFlowInfo.FlowCont = string(flowJsonCont) //流程执行体 + + if requestData.State == 3 { + // workFlowInfo.CurrentStep = executeWorkflow.Step + // workFlowInfo.NextStep = executeWorkflow.NextStep + //参与人去重 + var parUser []string + for _, v := range executeWorkflow.Participant { + if !publicmethod.IsInTrue[string](v, parUser) { + parUser = append(parUser, v) + } + } + workFlowInfo.Participants = strings.Join(parUser, ",") + nextNodeRunUser := executeWorkflow.NextNodePeople() + workFlowInfo.NextExecutor = strings.Join(nextNodeRunUser, ",") + if executeWorkflow.NextStep <= 0 { + workFlowInfo.Status = 4 + } + } + err = workFlowInfo.WriteCont() //写入执行工作流 + if err != nil { + var taskInfo customerForm.TaskRecord + taskInfo.EiteCont(map[string]interface{}{"`masters_key`": requestData.Id}, map[string]interface{}{"`status`": 1}) + publicmethod.Result(10001, err, c, "流程写入失败!请重新发起") + return + } + if executeWorkflow.NextStep <= 0 { + var taskCont customerForm.TaskRecord + taskCont.EiteCont(map[string]interface{}{"`masters_key`": requestData.Id}, map[string]interface{}{"`status`": 4, "`runFlowId`": uuid}) + } else { + var taskCont customerForm.TaskRecord + taskCont.EiteCont(map[string]interface{}{"`masters_key`": requestData.Id}, map[string]interface{}{"`status`": requestData.State, "`runFlowId`": uuid}) + } + publicmethod.Result(0, err, c) +} + +/* +* +@ 作者: 秦东 +@ 时间: 2024-12-17 10:40:52 +@ 功能: 执行工作流 +@ 参数 + + # + +@ 返回值 + + # + +@ 方法原型 + + # +*/ +func (a *ApiMethod) RunTaskWorkFlow(c *gin.Context) { + var requestData SubmitAppResults + err := c.ShouldBindJSON(&requestData) + if err != nil { + publicmethod.Result(100, err, c) + return + } + if requestData.Id == "" { + publicmethod.Result(100, err, c) + return + } + if requestData.AgreeOrRefuse == 0 { + requestData.AgreeOrRefuse = 2 + } + //step 1:获取流程执行情况 + var flowInfo customerForm.RunWorkflow + err = flowInfo.GetCont(map[string]interface{}{"`id`": requestData.Id}) + if err != nil { + publicmethod.Result(107, err, c) + return + } + if flowInfo.NextStep == 0 || flowInfo.Status != 3 { + publicmethod.Result(1, err, c, "此流程在不可审批状态!您的提交无效") + return + } + if len(requestData.FlowList) < 1 { //判断流程是否因表单条件而变化 + if flowInfo.FlowCont != "" { + err = json.Unmarshal([]byte(flowInfo.FlowCont), &requestData.FlowList) + if err != nil { + publicmethod.Result(1, err, c, "流程异常!您的提交无效") + return + } + } else { + publicmethod.Result(1, err, c, "流程异常!您的提交无效") + return + } + } + //step 2:获取操作人 + context, _ := c.Get(overall.MyContJwt) + var userCont modelshr.ManCont + userCont.GetLoginCont(context) + + var taskInfo customerForm.TaskRecord + taskInfo.GetCont(map[string]interface{}{"`runFlowId`": requestData.Id}, "`title`", "`creater`", `masters_key`, "`version_id`", "`flow_key`", "`flow_run_sing`", "`tableKey`", "`appKey`", "`runFlowId`", "`participant`") + //step 3:执行流程 + var runFlow RunWorkFlow + runFlow.Step = flowInfo.NextStep + runFlow.TotalSteps = len(requestData.FlowList) //流程总长度 + runFlow.Uuid = flowInfo.FlowKey + runFlow.RunUid = flowInfo.RunKey + runFlow.Participant = strings.Split(flowInfo.Participants, ",") + runFlow.FlowList = requestData.FlowList + + runFlow.SendWecharMsg.AppKey = taskInfo.AppKey + runFlow.SendWecharMsg.Creater = taskInfo.Creater + runFlow.SendWecharMsg.MastersKey = taskInfo.MastersKey + runFlow.SendWecharMsg.TableKey = taskInfo.TableKey + runFlow.SendWecharMsg.RunFlowId = taskInfo.RunFlowId + + runFlow.RunNodeHandle(userCont.Key, requestData.AgreeOrRefuse, requestData.Suggest) + if runFlow.IsRun { + publicmethod.Result(1, err, c, runFlow.Msg) + return + } + flowJsonCont, _ := json.Marshal(runFlow.FlowList) //将步进流转化成json流 + saveFlowInfo := publicmethod.MapOut[string]() //修改工作流 + + saveFlowInfo["`flow_cont`"] = flowJsonCont + saveFlowInfo["`current_step`"] = runFlow.Step + saveFlowInfo["`next_step`"] = runFlow.NextStep + if flowInfo.Participants != "" { + oldUser := strings.Split(flowInfo.Participants, ",") + runFlow.Participant = append(runFlow.Participant, oldUser...) + } + //参与人去重 + var parUser []string + for _, v := range runFlow.Participant { + if !publicmethod.IsInTrue[string](v, parUser) { + parUser = append(parUser, v) + } + } + saveFlowInfo["`participants`"] = strings.Join(parUser, ",") + + saveTaskInfo := publicmethod.MapOut[string]() //修改任务 + //参与人去重 + if len(parUser) > 0 { + oldTaskUser := strings.Split(taskInfo.Participant, ",") + for _, v := range parUser { + if !publicmethod.IsInTrue[string](v, oldTaskUser) { + oldTaskUser = append(oldTaskUser, v) + } + } + saveTaskInfo["`participant`"] = strings.Join(parUser, ",") + } + + //抄送人 + if flowInfo.MakeCopy != "" { + oldCopyUser := strings.Split(flowInfo.MakeCopy, ",") + runFlow.MakeCopy = append(runFlow.MakeCopy, oldCopyUser...) + } + //抄送人 去重 + var copyMan []string + for _, v := range runFlow.MakeCopy { + if !publicmethod.IsInTrue[string](v, copyMan) { + copyMan = append(copyMan, v) + } + } + saveFlowInfo["`makeCopy`"] = strings.Join(copyMan, ",") + //下一步执行人 + nextRunUser := runFlow.GainNextStepUser(runFlow.NextStep) + saveFlowInfo["`next_executor`"] = strings.Join(nextRunUser, ",") + saveFlowInfo["`update_time`"] = time.Now().Unix() + if requestData.AgreeOrRefuse == 1 { + if runFlow.NextStep != 0 { + saveFlowInfo["`status`"] = 3 + saveTaskInfo["`status`"] = 3 + } else { + saveFlowInfo["`status`"] = 4 + saveTaskInfo["`status`"] = 4 + taskmanagement.JudgeEditFlow(flowInfo.FlowKey) + } + } else { + saveFlowInfo["`runKey`"] = publicmethod.GetUUid(6) + if runFlow.Step != 1 { + saveFlowInfo["`status`"] = 3 + } else { + saveFlowInfo["`status`"] = 2 + saveTaskInfo["`status`"] = 1 + } + } + saveTaskInfo["`edit_time`"] = time.Now().Unix() + gordb := overall.CONSTANT_DB_CustomerForm.Begin() + flowErr := gordb.Model(&customerForm.RunWorkflow{}).Where(map[string]interface{}{"`id`": requestData.Id}).Updates(saveFlowInfo).Error + taskErr := gordb.Model(&customerForm.TaskRecord{}).Where("`runFlowId` = ?", requestData.Id).Updates(saveTaskInfo).Error + if flowErr != nil || taskErr != nil { + gordb.Rollback() + publicmethod.Result(100, err, c, "数据提交失败!请重新提交!") + return + } + gordb.Commit() + publicmethod.Result(0, err, c) + // sendMap := publicmethod.MapOut[string]() + // sendMap["runFlow"] = runFlow + // sendMap["saveFlowInfo"] = saveFlowInfo + // sendMap["saveTaskInfo"] = saveTaskInfo + // sendMap["nextStep"] = runFlow.NextStep + // publicmethod.Result(1, sendMap, c, runFlow.Msg) +} + +/* +* +@ 作者: 秦东 +@ 时间: 2024-12-19 10:01:12 +@ 功能: 获取指定节点执行人 +@ 参数 + + # + +@ 返回值 + + # + +@ 方法原型 + + # +*/ +func (r *RunWorkFlow) GainNextStepUser(stepNode int) (userAry []string) { + if stepNode <= 0 { + return + } + for _, v := range r.FlowList { + if v.Step == stepNode { + for _, op := range v.Operator { + if !publicmethod.IsInTrue[string](op.Id, userAry) { + userAry = append(userAry, op.Id) + } + } + } + } + return +} diff --git a/api/version1/workFlow/flowNode.go b/api/version1/workFlow/flowNode.go new file mode 100644 index 0000000..e8d4cb1 --- /dev/null +++ b/api/version1/workFlow/flowNode.go @@ -0,0 +1,189 @@ +package workflow + +import "appPlatform/overall/publicmethod" + +/** +@ 作者: 秦东 +@ 时间: 2024-04-09 10:56:48 +@ 功能: 工作流结构主体 +*/ +type FlowMainBody struct { + TableId string `json:"tableId"` //工作流识别吗 + WorkFlowDef WorkFlowDefInfo `json:"workFlowDef"` //工作流主体属性 + DirectorMaxLevel int `json:"directorMaxLevel"` //审批主管最大层级 + FlowPermission []FlowPermissionInfo `json:"flowPermission"` //流程发起人 + NodeConfig NodePublicInfo `json:"nodeConfig"` //节点信息内容 +} + +//工作流主体属性 +type WorkFlowDefInfo struct { + FormKey string `json:"formKey"` //关联操作ID + publicmethod.PublicName //姓名 +} + +//流程节点 +type NodePublicInfo struct { + NodePublicInfoES + Settype int `json:"settype"` // 审批人设置 1:指定成员; 2:主管;3:行政岗位; 4:发起人自选; 5:发起人自己;6:连续多级主管;7:指定前置审批为本节点设置审批人;8:表单字段;9:权限矩阵 + SelectMode int `json:"selectMode"` //审批人数 1选一个人 2选多个人 + SelectRange int `json:"selectRange"` //选择范围 1.全公司 2指定成员 3指定角色 + DirectorLevel int `json:"directorLevel"` //审批终点 最高层主管数 + ExamineMode int `json:"examineMode"` //多人审批时采用的审批方式 1:依次审批; 2:会签;3:非会签 + NoHanderAction int `json:"noHanderAction"` //审批人为空时 1自动审批通过/不允许发起 2转交给审核管理员 + ExamineEndDirectorLevel int `json:"examineEndDirectorLevel"` //审批终点 第n层主管 + SendBackNode string `json:"sendBackNode"` //退回哪个节点 + CustomNode string `json:"customNode"` //指定前置审批为本节点设置审批人 + + ConditionNodes []NodePublicInfoES `json:"conditionNodes"` //判断条件,当节点是路由时有效 + Executionaddress string `json:"executionaddress"` //第三方执行地址 + + ChildNode *NodePublicInfo `json:"childNode"` //子节点 + Matrix MatrixInfo `json:"matrix"` // + OrgList []int64 `json:"orgList"` //指定行政组织,由其负责人审批 + HelpTips string `json:"helpTips"` //节点帮助说明 +} + +type NodePublicInfoES struct { + NodeNumber string `json:"nodeNumber"` //节点识别符 + NodeName string `json:"nodeName"` //节点名称 + Types int `json:"type"` //0:发起人;1:审批;2:抄送;3:执行人;4:条件;5:路由 + FromNode string `json:"fromNode"` //来源节点 + GotoNode []string `json:"gotoNode"` //流向节点 + PriorityLevel int `json:"priorityLevel"` // 条件优先级 + Attribute int `json:"attribute"` //属性 1:申请人为基线;2:目标人为基线 + Errors bool `json:"error"` //当前审批是否通过校验 + CcSelfSelectFlag int `json:"ccSelfSelectFlag"` //允许发起人自选抄送人(0:不允许;1:允许) + NodeUserList []NodeUserListInfo `json:"nodeUserList"` //操作人 + ConditionList []ConditionListInfo `json:"conditionList"` //判断条件主体 + ChildNode *NodePublicInfo `json:"childNode"` //子节点 +} + +//节点执行人 +type NodeUserListInfo struct { + FlowPermissionInfo + publicmethod.CommonId[string] + publicmethod.PublicName + Options []OptionsInfo `json:"options"` //可选项(用于关联表单使用) + IsCheckbox bool `json:"isCheckbox"` //结果值多选 +} + +//人员;角色;行政组织;职务通用结构体 +type FlowPermissionInfo struct { + Types int `json:"type"` //1:人员;2:角色;3:行政组织;4:职务 + TargetId string `json:"targetId"` //相关内容识别符 + publicmethod.PublicName //相关内容名称 + Icon string `json:"icon"` //头像 + IconToBase64 string `json:"iconToBase64"` //头像Base64 +} + +//矩阵信息 +type MatrixInfo struct { + MatrixId int64 `json:"matrixid"` + FactorId int64 `json:"factorid"` + OutcomeId int64 `json:"outcomeid"` + MatrixName string `json:"matrixName"` + FactorName string `json:"factorName"` + OutcomeName string `json:"outcomeName"` +} + +//判断条件主体 +type ConditionListInfo struct { + publicmethod.CommonId[int] //条件顺序 + publicmethod.PublicName //条件名称 + Factorid string `json:"factorid"` //条件识别字段 + Types int `json:"type"` //条件类型:1:人员、行政组织、角色;2:自定义字段;3:关联表单字段; + Isok bool `json:"isok"` //页面渲染使用 + IsCheckbox bool `json:"isCheckbox"` //结果值多选 + Options []OptionsInfo `json:"options"` //可选项(用于关联表单使用) + Oneanswer string `json:"oneanswer"` //可选项锚定值(用于关联表单使用) + Answers []string `json:"answers"` //可选项锚定值(用于关联表单使用) + CustomFields []CustomFieldsInfo `json:"customFields"` //自定义字段条件主体 + NodeUserList []FlowPermissionInfo `json:"nodeUserList"` //人员、行政组织、角色为条件主体 +} + +//可选项(用于关联表单使用) +type OptionsInfo struct { + Label string `json:"label"` //名称 + Value string `json:"value"` //值 +} + +//自定义字段条件主体 +type CustomFieldsInfo struct { + Wordfield string `json:"wordfield"` //判断字段 + OptType string `json:"optType"` //判定方法(1:小于;2:大于;3:小于等于;4:等于;5:大于等于;6:介于两数之间;7:包含;8:不包含) + LeftVal string `json:"leftval"` //左侧值 + LeftOptType string `json:"leftoptType"` //OptType值为6时;左侧判定方法 + RightOptType string `json:"rightoptType"` //OptType值为6时;右侧判定方法 + RightVal string `json:"rightval"` //OptType值为6时;右侧值 +} + +//输出工作流相关操作 +type SendFlowInfo struct { + Step int `json:"Step"` //当前执行第几步 + NodeKey string `json:"nodeKey"` //当前节点标识 + NextStep int `json:"nextStep"` //下一步执行第几步 + FlowList []RunFlow `json:"flowList"` //流程主体 +} + +/** +@ 作者: 秦东 +@ 时间: 2023-11-01 09:12:18 +@ 功能: 流程执行体 +*/ +type RunFlow struct { + Step int `json:"step"` //步骤 + Types int `json:"type"` //0:发起节点;1:审批节点;2:抄送;3:执行节点 + NodeKey string `json:"nodeKey"` //节点识别符 + NodeName string `json:"nodeName"` //节点名称 + Status int `json:"status"` //1:未到达;2:已审批;3:已驳回;4:再次审批 + FromNode string `json:"fromnode"` //来至哪个节点 + ArriveNode string `json:"arrivenode"` //到哪个节点 + GoBackNode string `json:"gobacknode"` //驳回返回节点 + ExamineMode int `json:"examinemode"` //多人审批时采用的审批方式。0:无操作 1依次审批 2会签 3:非会签 + NoHanderAction int `json:"nohanderaction"` //审批人为空时 1自动审批通过/不允许发起 2转交给审核管理员 + CustomNode string `json:"customNode"` //由哪个节点指定本节点审批人 + JudgeList bool `json:"judgelist"` //是否可自己选中操作人 + Operator []OperatorList `json:"operator"` //操作人 + PendPers []OperatorList `json:"pendpers"` //操作人ssss + RunType int `json:"runtype"` //运行时选择 0:禁闭;1:发起人自选,2:发起人自己,3:有选中得节点指定,4:抄送节点 + RunScope int `json:"runscope"` //运行时选择范围 0:不可选,1:本公司;2:本部门;当RunType = 4时:1:自选;非1:不可自选 + // Operational bool `json:"operational"` //是否可提交审批意见 + HelpTips string `json:"helpTips"` //节点帮助说明 +} + +//操作人 +type OperatorList struct { + Id string `json:"id"` //操作人ID + Types int `json:"type"` //1:人员;2:角色;3:行政组织 + Name string `json:"name"` //操作人姓名 + Number string `json:"number"` //操作人工号 + Icon string `json:"icon"` //操作人头像 + IconBase64 string `json:"iconbase64"` //操作人头像 + Wechat string `json:"wechat"` //微信Openid + DepartmentId int64 `json:"departmentid"` //分厂Id + DepartmentName string `json:"departmentname"` //分厂名称 + PostId int64 `json:"postid"` //职务Id + PostName string `json:"postname"` //职务名称 + Tema int64 `json:"tema"` //班组Id + TemaName string `json:"temaname"` //班组名称 + LogList []LogList `json:"log"` //操作记录 + NoEdit bool `json:"noedit"` //不可删除 + Tel string `json:"tel"` //电话 + CompanyName string `json:"companyName"` //公司 +} + +// 节点操作人操作记录 +type LogList struct { + State int `json:"state"` //状态 1、未操作;2、通过;3、驳回;4、已查看 + TimeVal string `json:"time"` + Cause string `json:"cause"` //审批意见 + Enclosure []EnclosureFormat `json:"enclosure"` //附件 + UID string `json:"uid"` //当前执行识别符 +} + +// 附件格式 +type EnclosureFormat struct { + FileName string `json:"filename"` //附件名称 + FilePath string `json:"filepath"` //附件地址 + Type int `json:"type"` //附件类型 +} diff --git a/api/version1/workFlow/flowType.go b/api/version1/workFlow/flowType.go new file mode 100644 index 0000000..481796b --- /dev/null +++ b/api/version1/workFlow/flowType.go @@ -0,0 +1,41 @@ +package workflow + +import "appPlatform/overall/publicmethod" + +// 启动流程 +type StartWorkFlow struct { + publicmethod.PublicId + FlowList []RunFlow `json:"flowList"` //流程主体 + State int `json:"state"` //状态状态:1、草稿;2:驳回;3:审批中;4:归档;5:删除 +} + +// 流程执行 +type RunWorkFlow struct { + Step int //执行哪一步 + NextStep int //下一步 + TotalSteps int //总步数 + FlowList []RunFlow //流程 + Participant []string //参与人 + MakeCopy []string //抄送人 + NewFlowList []RunFlow //流程 + Uuid int64 // + RunUid int64 //执行Uid + IsRun bool //是否结束执行 + Msg string //执行说明 + SendWecharMsg SendWecharMsgMap +} + +type SendWecharMsgMap struct { + MastersKey int64 + TableKey int64 + AppKey int64 + RunFlowId int64 + Creater int64 +} + +type SubmitAppResults struct { + publicmethod.PublicId + AgreeOrRefuse int `json:"agreeOrRefuse"` //1:同意;2:驳回 + Suggest string `json:"suggest"` //审批意见 + FlowList []RunFlow `json:"flowlist"` //执行流程 +} diff --git a/api/version1/workFlow/type.go b/api/version1/workFlow/type.go new file mode 100644 index 0000000..7351b27 --- /dev/null +++ b/api/version1/workFlow/type.go @@ -0,0 +1,8 @@ +package workflow + +import "sync" + +// 协程设置 +var syncSeting = sync.WaitGroup{} + +type ApiMethod struct{} diff --git a/api/version1/workWechat/msgMap.go b/api/version1/workWechat/msgMap.go new file mode 100644 index 0000000..0f06d3c --- /dev/null +++ b/api/version1/workWechat/msgMap.go @@ -0,0 +1,662 @@ +package workWechat + +import ( + "appPlatform/overall/publicmethod" + "errors" +) + +// 卡片来源样式信息,不需要来源样式可不填写 +func (s *Source) Source() (mapAry map[string]interface{}, isTrue bool) { + mapAryCont := publicmethod.MapOut[string]() + if s.IconUrl != "" { + mapAryCont["icon_url"] = s.IconUrl + } + if s.Desc != "" { + mapAryCont["desc"] = s.Desc + } + if s.DescColor != 0 { + mapAryCont["desc_color"] = s.DescColor + } + if len(mapAryCont) > 0 { + isTrue = true + mapAry = mapAryCont + } else { + isTrue = false + } + return +} + +// 发送信息公用头部 +func (m *MsgCommon) MsgCommon() (mapAry map[string]interface{}, err error) { + if m.Touser == "" && m.Toparty == "" && m.Totag == "" { + err = errors.New("没有要接收信息的人员!不可发送信息") + return + } + if m.Msgtype == "" { + err = errors.New("未知消息类型!不可发送信息") + return + } + if m.Agentid == 0 { + err = errors.New("未知企业应用的id!不可发送信息") + return + } + mapAryCont := publicmethod.MapOut[string]() + mapAryCont["msgtype"] = m.Msgtype + if m.Msgtype != "" && m.Msgtype != "miniprogram_notice" { + mapAryCont["agentid"] = m.Agentid + } + + if m.Touser != "" { + mapAryCont["touser"] = m.Touser + } + if m.Toparty != "" { + mapAryCont["toparty"] = m.Toparty + } + if m.Totag != "" { + mapAryCont["totag"] = m.Totag + } + mapAry = mapAryCont + return +} + +// 发送信息公用底部 +func (m *MsgCommonFooter) MsgCommonFooter(msgType string) map[string]interface{} { + mapAryCont := publicmethod.MapOut[string]() + + switch msgType { + case "text", "mpnews": + mapAryCont["safe"] = m.Safe + mapAryCont["enable_id_trans"] = m.EnableIdTrans + mapAryCont["enable_duplicate_check"] = m.EnableDuplicateCheck + if m.DuplicateCheckInterval == 0 { + mapAryCont["duplicate_check_interval"] = 1800 + } else { + mapAryCont["duplicate_check_interval"] = m.DuplicateCheckInterval + } + case "image", "video", "file": + mapAryCont["safe"] = m.Safe + mapAryCont["enable_duplicate_check"] = m.EnableDuplicateCheck + if m.DuplicateCheckInterval == 0 { + mapAryCont["duplicate_check_interval"] = 1800 + } else { + mapAryCont["duplicate_check_interval"] = m.DuplicateCheckInterval + } + case "voice", "markdown": + mapAryCont["enable_duplicate_check"] = m.EnableDuplicateCheck + if m.DuplicateCheckInterval == 0 { + mapAryCont["duplicate_check_interval"] = 1800 + } else { + mapAryCont["duplicate_check_interval"] = m.DuplicateCheckInterval + } + case "textcard", "news", "miniprogram_notice", "template_card": //文本卡片消息 + mapAryCont["enable_id_trans"] = m.EnableIdTrans + mapAryCont["enable_duplicate_check"] = m.EnableDuplicateCheck + if m.DuplicateCheckInterval == 0 { + mapAryCont["duplicate_check_interval"] = 1800 + } else { + mapAryCont["duplicate_check_interval"] = m.DuplicateCheckInterval + } + default: + + } + + return mapAryCont +} + +// 卡片右上角更多操作按钮 +func (a *Action_menu) ActionMenu() (mapAry map[string]interface{}, isTrue bool) { + var actionList []interface{} + for _, v := range a.ActionList { + mapAry, mapisTrue := v.ActionList() + if mapisTrue { + actionList = append(actionList, mapAry) + } + } + mapAryCont := publicmethod.MapOut[string]() + if a.Desc != "" { + mapAryCont["desc"] = a.Desc + } + if len(actionList) > 0 { + mapAryCont["action_list"] = actionList + } + if len(mapAryCont) > 0 { + isTrue = true + mapAry = mapAryCont + } else { + isTrue = false + } + return +} + +func (a *Action_list) ActionList() (mapAry map[string]interface{}, isTrue bool) { + isTrue = false + if a.Key == "" || a.Text == "" { + return + } + mapAryCont := publicmethod.MapOut[string]() + mapAryCont["key"] = a.Key + mapAryCont["text"] = a.Text + mapAry = mapAryCont + isTrue = true + return +} + +// 一级标题 +func (m *Main_title) Main_title() (mapAry map[string]interface{}, isTrue bool) { + mapAryCont := publicmethod.MapOut[string]() + if m.Title != "" { + mapAryCont["title"] = m.Title + } + if m.Desc != "" { + mapAryCont["desc"] = m.Desc + } + if len(mapAryCont) > 0 { + isTrue = true + mapAry = mapAryCont + } else { + isTrue = false + } + return +} + +// 引用文献样式 +func (q *Quote_area) Quote_area() (mapAry map[string]interface{}, isTrue bool) { + mapAryCont := publicmethod.MapOut[string]() + mapAryCont["type"] = q.Type + if q.Url != "" { + mapAryCont["url"] = q.Url + } + if q.Appid != "" { + mapAryCont["appid"] = q.Type + } + if q.Pagepath != "" { + mapAryCont["pagepath"] = q.Pagepath + } + if q.Title != "" { + mapAryCont["title"] = q.Title + } + if q.Quote_text != "" { + mapAryCont["quote_text"] = q.Quote_text + } + if len(mapAryCont) > 1 { + isTrue = true + mapAry = mapAryCont + } else { + isTrue = false + } + return +} + +// 二级标题+文本列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过6 +func (h *Horizontal_content_list) Horizontal_content_list() (mapAry map[string]interface{}, isTrue bool) { + mapAryCont := publicmethod.MapOut[string]() + mapAryCont["type"] = h.Type + mapAryCont["keyname"] = h.Keyname + + if h.UserId != "" { + mapAryCont["userid"] = h.UserId + } + if h.Value != "" { + mapAryCont["value"] = h.Value + } + if h.Url != "" { + mapAryCont["url"] = h.Url + } + if h.MediaId != "" { + mapAryCont["media_id"] = h.MediaId + } + if len(mapAryCont) > 2 { + isTrue = true + mapAry = mapAryCont + } else { + isTrue = false + } + switch h.Type { + case 1: + if h.Url == "" { + isTrue = false + } + case 2: + if h.MediaId == "" { + isTrue = false + } + case 3: + if h.UserId == "" { + isTrue = false + } + } + + return +} + +// 跳转指引样式的列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过3 +func (j *Jump_list) Jump_list() (mapAry map[string]interface{}, isTrue bool) { + mapAryCont := publicmethod.MapOut[string]() + mapAryCont["type"] = j.Type + mapAryCont["title"] = j.Title + + if j.Url != "" { + mapAryCont["url"] = j.Url + } + if j.Appid != "" { + mapAryCont["appid"] = j.Appid + } + if j.Pagepath != "" { + mapAryCont["pagepath"] = j.Pagepath + } + + if len(mapAryCont) > 2 { + isTrue = true + mapAry = mapAryCont + } else { + isTrue = false + } + switch j.Type { + case 1: + if j.Url == "" { + isTrue = false + } + case 2: + if j.Appid == "" { + isTrue = false + } + } + return +} + +func (c *Card_action) Card_action() (mapAry map[string]interface{}, isTrue bool) { + mapAryCont := publicmethod.MapOut[string]() + mapAryCont["type"] = c.Type + if c.Url != "" { + mapAryCont["url"] = c.Url + } + if c.Appid != "" { + mapAryCont["appid"] = c.Appid + } + if c.Pagepath != "" { + mapAryCont["pagepath"] = c.Pagepath + } + + if len(mapAryCont) > 1 { + isTrue = true + mapAry = mapAryCont + } else { + isTrue = false + } + switch c.Type { + case 1: + if c.Url == "" { + isTrue = false + } + case 2: + if c.Appid == "" { + isTrue = false + } + } + return +} + +/* +* +@ 作者: 秦东 +@ 时间: 2024-12-10 14:14:44 +@ 功能: +*/ +func (e *Emphasis_content) Emphasis_content() (mapAry map[string]interface{}, isTrue bool) { + mapAryCont := publicmethod.MapOut[string]() + if e.Title != "" { + mapAryCont["title"] = e.Title + } + if e.Desc != "" { + mapAryCont["desc"] = e.Desc + } + if len(mapAryCont) > 0 { + isTrue = true + mapAry = mapAryCont + } else { + isTrue = false + } + return +} + +/* +* +@ 作者: 秦东 +@ 时间: 2024-12-10 14:39:13 +@ 功能:左图右文样式,news_notice类型的卡片,card_image和image_text_area两者必填一个字段,不可都不填 +*/ +func (i *Image_text_area) Image_text_area() (mapAry map[string]interface{}, isTrue bool) { + mapAryCont := publicmethod.MapOut[string]() + mapAryCont["type"] = i.Type + mapAryCont["image_url"] = i.ImageUrl + if i.Url != "" { + mapAryCont["url"] = i.Url + } + if i.Title != "" { + mapAryCont["title"] = i.Title + } + if i.Desc != "" { + mapAryCont["desc"] = i.Desc + } + if i.Appid != "" { + mapAryCont["appid"] = i.Type + } + if i.Pagepath != "" { + mapAryCont["pagepath"] = i.Pagepath + } + if len(mapAryCont) > 2 { + isTrue = true + mapAry = mapAryCont + } else { + isTrue = false + } + switch i.Type { + case 1: + if i.Url == "" { + isTrue = false + } + case 2: + if i.Appid == "" { + isTrue = false + } + } + return +} + +/* +* +@ 作者: 秦东 +@ 时间: 2024-12-10 14:48:27 +@ 功能: 图片样式,news_notice类型的卡片,card_image和image_text_area两者必填一个字段,不可都不填 +*/ +func (c *Card_image) Card_image() (mapAry map[string]interface{}, isTrue bool) { + mapAryCont := publicmethod.MapOut[string]() + mapAryCont["url"] = c.Url + if c.AspectRatio != 0 { + mapAryCont["url"] = c.AspectRatio + } + if c.Url == "" { + isTrue = false + return + } + if len(mapAryCont) > 0 { + isTrue = true + mapAry = mapAryCont + } else { + isTrue = false + } + return +} + +/* +* +@ 作者: 秦东 +@ 时间: 2024-12-10 14:54:07 +@ 功能: 卡片二级垂直内容,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过4 +*/ +func (v *Vertical_content_list) Vertical_content_list() (mapAry map[string]interface{}, isTrue bool) { + mapAryCont := publicmethod.MapOut[string]() + mapAryCont["title"] = v.Title + if v.Desc != "" { + mapAryCont["desc"] = v.Desc + } + if v.Title == "" { + isTrue = false + return + } + if len(mapAryCont) > 0 { + isTrue = true + mapAry = mapAryCont + } else { + isTrue = false + } + return +} + +/* +* +@ 作者: 秦东 +@ 时间: 2024-12-10 15:01:33 +@ 功能: 下拉式的选择器 +*/ +func (b *Button_selection) Button_selection() (mapAry map[string]interface{}, isTrue bool) { + isTrue = false + mapAryCont := publicmethod.MapOut[string]() + mapAryCont["question_key"] = b.QuestionKey + if b.Title != "" { + mapAryCont["title"] = b.Title + } + if b.SelectedId != "" { + mapAryCont["selected_id"] = b.SelectedId + } + if len(b.OptionList) > 0 { + + var optList []interface{} + for _, v := range b.OptionList { + optVal, optIsOk := v.Option_list() + if optIsOk { + optList = append(optList, optVal) + } + } + if len(optList) > 0 { + isTrue = true + mapAryCont["option_list"] = optList + } + } + if b.QuestionKey == "" { + isTrue = false + } + mapAry = mapAryCont + return +} + +// 选项列表,下拉选项不超过 10 个,最少1个 +func (o *Option_list) Option_list() (mapAry map[string]interface{}, isTrue bool) { + mapAryCont := publicmethod.MapOut[string]() + if o.Id != "" { + mapAryCont["id"] = o.Id + } + if o.Text != "" { + mapAryCont["text"] = o.Text + } + if len(mapAryCont) >= 2 { + isTrue = true + mapAry = mapAryCont + } else { + isTrue = false + } + return +} + +/* +* +@ 作者: 秦东 +@ 时间: 2024-12-10 15:14:04 +@ 功能: 按钮列表,列表长度不超过6 +*/ +func (b *Button_list) Button_list() (mapAry map[string]interface{}, isTrue bool) { + mapAryCont := publicmethod.MapOut[string]() + mapAryCont["type"] = b.Type + mapAryCont["text"] = b.Text + if b.Style != 0 { + mapAryCont["style"] = b.Style + } + if b.Key != "" { + mapAryCont["key"] = b.Key + } + if b.Url != "" { + mapAryCont["url"] = b.Url + } + isTrue = true + if b.Text == "" { + isTrue = false + } else { + if b.Type == 1 && b.Url == "" { + isTrue = false + } + if b.Type == 0 && b.Key == "" { + isTrue = false + } + } + mapAry = mapAryCont + return +} + +/* +* +@ 作者: 秦东 +@ 时间: 2024-12-10 15:24:41 +@ 功能: 选择题样式 +*/ +func (c *Checkbox) Checkbox() (mapAry map[string]interface{}, isTrue bool) { + mapAryCont := publicmethod.MapOut[string]() + mapAryCont["mode"] = c.Mode + mapAryCont["question_key"] = c.QuestionKey + + if len(c.OptionList) > 0 { + + var optList []interface{} + for _, v := range c.OptionList { + optVal, optIsOk := v.OptionList() + if optIsOk { + optList = append(optList, optVal) + } + } + if len(optList) > 0 { + isTrue = true + mapAryCont["option_list"] = optList + } + } + if c.QuestionKey == "" || len(c.OptionList) < 0 { + isTrue = false + } else { + isTrue = true + } + return +} + +func (o *OptionList) OptionList() (mapAry map[string]interface{}, isTrue bool) { + mapAryCont := publicmethod.MapOut[string]() + mapAryCont["id"] = o.Id + mapAryCont["text"] = o.Text + mapAryCont["is_checked"] = o.IsChecked + if o.Id == "" || o.Text == "" { + isTrue = false + } else { + isTrue = true + } + return +} + +/** +@ 作者: 秦东 +@ 时间: 2024-12-10 15:53:25 +@ 功能: 提交按钮样式 +*/ + +func (s *Submit_button) Submit_button() (mapAry map[string]interface{}, isTrue bool) { + mapAryCont := publicmethod.MapOut[string]() + if s.Text != "" { + mapAryCont["text"] = s.Text + } + if s.Key != "" { + mapAryCont["key"] = s.Key + } + if len(mapAryCont) >= 2 { + isTrue = true + mapAry = mapAryCont + } else { + isTrue = false + } + return +} + +/* +* +@ 作者: 秦东 +@ 时间: 2024-12-11 14:19:04 +@ 功能: 图文消息,一个图文消息支持1到8条图文 +*/ +func (a *ArticlesList) Articles_list() (mapAry map[string]interface{}, isTrue bool) { + mapAryCont := publicmethod.MapOut[string]() + if a.Title != "" { + mapAryCont["title"] = a.Title + + if a.Description != "" { + mapAryCont["description"] = a.Description + } + if a.Url != "" { + mapAryCont["url"] = a.Url + } + if a.PicUrl != "" { + mapAryCont["picurl"] = a.PicUrl + } + if a.AppId != "" { + mapAryCont["appid"] = a.AppId + } + if a.Pagepath != "" { + mapAryCont["pagepath"] = a.Pagepath + } + } + + if len(mapAryCont) > 0 { + isTrue = true + mapAry = mapAryCont + } else { + isTrue = false + } + return +} + +/* +* +@ 作者: 秦东 +@ 时间: 2024-12-11 14:28:26 +@ 功能: +*/ +func (a *ArticlesListMpn) Articles_list_mpn() (mapAry map[string]interface{}, isTrue bool) { + + if a.Title == "" && a.ThumbMediaId == "" && a.Content == "" { + isTrue = false + } else { + mapAryCont := publicmethod.MapOut[string]() + mapAryCont["title"] = a.Title + mapAryCont["thumb_media_id"] = a.ThumbMediaId + mapAryCont["pagepath"] = a.Content + + if a.Author != "" { + mapAryCont["author"] = a.Author + } + if a.ContentSourceUrl != "" { + mapAryCont["content_source_url"] = a.ContentSourceUrl + } + if a.Digest != "" { + mapAryCont["digest"] = a.Digest + } + if len(mapAryCont) > 0 { + isTrue = true + mapAry = mapAryCont + } else { + isTrue = false + } + } + return +} + +// 循环值 +func (c *Content_item) Content_item() (mapAry map[string]interface{}, isTrue bool) { + mapAryCont := publicmethod.MapOut[string]() + if c.Value != "" { + mapAryCont["key"] = c.Value + } + if c.Key != "" { + mapAryCont["url"] = c.Key + } + if len(mapAryCont) > 0 { + isTrue = true + mapAry = mapAryCont + } else { + isTrue = false + } + return +} diff --git a/api/version1/workWechat/msgMethod.go b/api/version1/workWechat/msgMethod.go new file mode 100644 index 0000000..bddf785 --- /dev/null +++ b/api/version1/workWechat/msgMethod.go @@ -0,0 +1,529 @@ +package workWechat + +import ( + "appPlatform/overall/publicmethod" + "errors" +) + +/* +* +@ 作者: 秦东 +@ 时间: 2024-12-11 13:17:09 +@ 功能: 模板卡片消息 +*/ +func (s *SendMessage) TemplateCardMethod() (templateCardMap map[string]interface{}, err error) { + templateCard := publicmethod.MapOut[string]() //模版输出 + if s.TemplateCard.TemplateCommon.TaskId == "" { + err = errors.New("没有任务id!不可发送信息") + return + } else { + templateCard["task_id"] = s.TemplateCard.TemplateCommon.TaskId //任务id,同一个应用任务id不能重复,只能由数字、字母和“_-@”组成,最长128字节 + } + sourceMap, sourceIsTrue := s.TemplateCard.TemplateCommon.Source.Source() //卡片来源样式信息,不需要来源样式可不填写 + if sourceIsTrue { + sourceMapAry := publicmethod.MapOut[string]() + for i, v := range sourceMap { + sourceMapAry[i] = v + } + templateCard["source"] = sourceMapAry + } + mainTitleMap, mainTitleIsTrue := s.TemplateCard.TemplateCommon.MainTitle.Main_title() //一级标题 + if mainTitleIsTrue { + mainTitleMapAry := publicmethod.MapOut[string]() + for i, v := range mainTitleMap { + mainTitleMapAry[i] = v + } + templateCard["main_title"] = mainTitleMapAry + } + templateCard["card_type"] = s.TemplateCard.TemplateCommon.CardType + switch s.TemplateCard.TemplateCommon.CardType { + case "text_notice": //文本类型 + actionMenuMap, actionMenuIsTrue := s.TemplateCard.TextTemplate.ActionMenu.ActionMenu() //卡片右上角更多操作按钮 + if actionMenuIsTrue { + actionMenuMapAry := publicmethod.MapOut[string]() + for i, v := range actionMenuMap { + actionMenuMapAry[i] = v + } + templateCard["action_menu"] = actionMenuMapAry + } + + quoteAreaMap, quoteAreaIsTrue := s.TemplateCard.TextTemplate.QuoteArea.Quote_area() //引用文献样式 + if quoteAreaIsTrue { + quoteAreaMapAry := publicmethod.MapOut[string]() + for i, v := range quoteAreaMap { + quoteAreaMapAry[i] = v + } + templateCard["quote_area"] = quoteAreaMapAry + } + if s.TemplateCard.TextTemplate.SubTitleText != "" { + templateCard["sub_title_text"] = s.TemplateCard.TextTemplate.SubTitleText //二级普通文本,建议不超过160个字 + } + if len(s.TemplateCard.TextTemplate.HorizontalContentList) > 0 { //二级标题+文本列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过6 + var horContList []interface{} + for _, v := range s.TemplateCard.TextTemplate.HorizontalContentList { + horCont, horContIsTrue := v.Horizontal_content_list() + if horContIsTrue { + horContList = append(horContList, horCont) + } + } + if len(horContList) > 0 { + templateCard["horizontal_content_list"] = horContList + } + } + if len(s.TemplateCard.TextTemplate.JumpList) > 0 { //跳转指引样式的列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过3 + var jumpListtList []interface{} + for _, v := range s.TemplateCard.TextTemplate.JumpList { + jumpCont, jumpContIsTrue := v.Jump_list() + if jumpContIsTrue { + jumpListtList = append(jumpListtList, jumpCont) + } + } + if len(jumpListtList) > 0 { + templateCard["jump_list"] = jumpListtList + } + } + // fmt.Printf("s.TemplateCard.TextTemplate.CardAction==>%v\n", s.TemplateCard.TextTemplate.CardAction) + //整体卡片的点击跳转事件,text_notice必填本字段 + cardMap, cardIsTrue := s.TemplateCard.TextTemplate.CardAction.Card_action() //卡片来源样式信息,不需要来源样式可不填写 + if cardIsTrue { + cardMapAry := publicmethod.MapOut[string]() + for i, v := range cardMap { + cardMapAry[i] = v + } + templateCard["card_action"] = cardMapAry + } else { + err = errors.New("没有任务整体卡片的点击跳转事件!不可发送信息") + return + } + empVal, empIsTrue := s.TemplateCard.TextTemplate.EmphasisContent.Emphasis_content() + if empIsTrue { + templateCard["emphasis_content"] = empVal + } + case "news_notice": //图文类型 + actionMenuMap, actionMenuIsTrue := s.TemplateCard.ActionMenu.ActionMenu() //卡片右上角更多操作按钮 + if actionMenuIsTrue { + actionMenuMapAry := publicmethod.MapOut[string]() + for i, v := range actionMenuMap { + actionMenuMapAry[i] = v + } + templateCard["action_menu"] = actionMenuMapAry + } + + quoteAreaMap, quoteAreaIsTrue := s.TemplateCard.QuoteArea.Quote_area() //引用文献样式 + if quoteAreaIsTrue { + quoteAreaMapAry := publicmethod.MapOut[string]() + for i, v := range quoteAreaMap { + quoteAreaMapAry[i] = v + } + templateCard["quote_area"] = quoteAreaMapAry + } + if len(s.TemplateCard.HorizontalContentList) > 0 { //二级标题+文本列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过6 + var horContList []interface{} + for _, v := range s.TemplateCard.HorizontalContentList { + horCont, horContIsTrue := v.Horizontal_content_list() + if horContIsTrue { + horContList = append(horContList, horCont) + } + } + if len(horContList) > 0 { + templateCard["horizontal_content_list"] = horContList + } + } + if len(s.TemplateCard.JumpList) > 0 { //跳转指引样式的列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过3 + var jumpListtList []interface{} + for _, v := range s.TemplateCard.JumpList { + jumpCont, jumpContIsTrue := v.Jump_list() + if jumpContIsTrue { + jumpListtList = append(jumpListtList, jumpCont) + } + } + if len(jumpListtList) > 0 { + templateCard["jump_list"] = jumpListtList + } + } + //整体卡片的点击跳转事件,text_notice必填本字段 + cardMap, cardIsTrue := s.TemplateCard.CardAction.Card_action() //卡片来源样式信息,不需要来源样式可不填写 + if cardIsTrue { + cardMapAry := publicmethod.MapOut[string]() + for i, v := range cardMap { + cardMapAry[i] = v + } + templateCard["card_action"] = cardMapAry + } else { + err = errors.New("没有任务整体卡片的点击跳转事件!不可发送信息") + return + } + imgAreaMap, imgAreaIsTrue := s.TemplateCard.ImageTemplate.ImageTextArea.Image_text_area() //左图右文样式,news_notice类型的卡片,card_image和image_text_area两者必填一个字段,不可都不填 + if imgAreaIsTrue { + imgAreaMapAry := publicmethod.MapOut[string]() + for i, v := range imgAreaMap { + imgAreaMapAry[i] = v + } + templateCard["image_text_area"] = imgAreaMapAry + } + cardImgMap, cardImgIsTrue := s.TemplateCard.ImageTemplate.CardImage.Card_image() //卡片二级垂直内容,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过4 + if cardImgIsTrue { + cardImgMapAry := publicmethod.MapOut[string]() + for i, v := range cardImgMap { + cardImgMapAry[i] = v + } + templateCard["card_image"] = cardImgMapAry + } + if len(s.TemplateCard.ImageTemplate.VerticalContentList) > 0 { + var verContList []interface{} + for _, v := range s.TemplateCard.ImageTemplate.VerticalContentList { + verCont, horContIsTrue := v.Vertical_content_list() + if horContIsTrue { + verContList = append(verContList, verCont) + } + } + if len(verContList) > 0 { + templateCard["vertical_content_list"] = verContList + } + } + case "button_interaction": //按钮类型 + actionMenuMap, actionMenuIsTrue := s.TemplateCard.ActionMenu.ActionMenu() //卡片右上角更多操作按钮 + if actionMenuIsTrue { + actionMenuMapAry := publicmethod.MapOut[string]() + for i, v := range actionMenuMap { + actionMenuMapAry[i] = v + } + templateCard["action_menu"] = actionMenuMapAry + } + + quoteAreaMap, quoteAreaIsTrue := s.TemplateCard.QuoteArea.Quote_area() //引用文献样式 + if quoteAreaIsTrue { + quoteAreaMapAry := publicmethod.MapOut[string]() + for i, v := range quoteAreaMap { + quoteAreaMapAry[i] = v + } + templateCard["quote_area"] = quoteAreaMapAry + } + if s.TemplateCard.SubTitleText != "" { + templateCard["sub_title_text"] = s.TemplateCard.SubTitleText //二级普通文本,建议不超过160个字 + } + if len(s.TemplateCard.HorizontalContentList) > 0 { //二级标题+文本列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过6 + var horContList []interface{} + for _, v := range s.TemplateCard.HorizontalContentList { + horCont, horContIsTrue := v.Horizontal_content_list() + if horContIsTrue { + horContList = append(horContList, horCont) + } + } + if len(horContList) > 0 { + templateCard["horizontal_content_list"] = horContList + } + } + //整体卡片的点击跳转事件,text_notice必填本字段 + cardMap, cardIsTrue := s.TemplateCard.CardAction.Card_action() //卡片来源样式信息,不需要来源样式可不填写 + if cardIsTrue { + cardMapAry := publicmethod.MapOut[string]() + for i, v := range cardMap { + cardMapAry[i] = v + } + templateCard["card_action"] = cardMapAry + } else { + err = errors.New("没有任务整体卡片的点击跳转事件!不可发送信息") + return + } + butVal, butIsTrue := s.TemplateCard.ButtonTemplate.ButtonSelection.Button_selection() //下拉式的选择器 + if butIsTrue { + templateCard["button_selection"] = butVal + } + + if len(s.TemplateCard.ButtonTemplate.ButtonList) > 0 { //按钮列表,列表长度不超过6 + var butListContList []interface{} + for _, v := range s.TemplateCard.ButtonTemplate.ButtonList { + butListCont, butListIsTrue := v.Button_list() + if butListIsTrue { + butListContList = append(butListContList, butListCont) + } + } + if len(butListContList) > 0 { + templateCard["button_list"] = butListContList + } + } else { + err = errors.New("您没有设定操作按钮!不可发送信息") + return + } + case "vote_interaction": //投票类型 + cheVal, cheIsTrue := s.TemplateCard.VoteTemplate.CheckBox.Checkbox() + if cheIsTrue { + templateCard["checkbox"] = cheVal + } else { + err = errors.New("您没有设定选择题!不可发送信息") + return + } + subButVal, subButIsTrue := s.TemplateCard.VoteTemplate.SubmitButton.Submit_button() + if subButIsTrue { + templateCard["submit_button"] = subButVal + } else { + err = errors.New("您没有设定提交按钮!不可发送信息") + return + } + case "multiple_interaction": //多项选择型 + if len(s.TemplateCard.MultipleTemplate.SelectList) > 0 { + var selectList []interface{} + for _, v := range s.TemplateCard.MultipleTemplate.SelectList { + selListCont, selListIsTrue := v.Button_selection() + if selListIsTrue { + selectList = append(selectList, selListCont) + } + } + if len(selectList) > 0 { + templateCard["select_list"] = selectList + } + } else { + err = errors.New("您没有设定下拉式的选择器列表数据!不可发送信息") + return + } + subButVal, subButIsTrue := s.TemplateCard.SubmitButton.Submit_button() + if subButIsTrue { + templateCard["submit_button"] = subButVal + } else { + err = errors.New("您没有设定提交按钮!不可发送信息") + return + } + default: + err = errors.New("没有选择模板卡片类型!不可发送信息") + return + } + templateCardMap = templateCard + return +} + +/* +* +@ 作者: 秦东 +@ 时间: 2024-12-11 13:40:45 +@ 功能: 文本消息 +*/ +func (s *SendMessage) TextMethod() (msgMap map[string]interface{}, err error) { + msgInfo := publicmethod.MapOut[string]() //模版输出 + if s.TextMsg.Content != "" { + msgInfo["content"] = s.TextMsg.Content + } else { + err = errors.New("没有要发送的消息内容!不可发送信息") + return + } + msgMap = msgInfo + return +} + +/* +* +@ 作者: 秦东 +@ 时间: 2024-12-11 13:40:45 +@ 功能: 图片消息 +*/ +func (s *SendMessage) ImageMethod() (msgMap map[string]interface{}, err error) { + msgInfo := publicmethod.MapOut[string]() //模版输出 + if s.ImageMsg.MediaId != "" { + msgInfo["media_id"] = s.ImageMsg.MediaId + } else { + err = errors.New("没有要发送的图片媒体文件id!不可发送信息") + return + } + msgMap = msgInfo + return +} + +/* +* +@ 作者: 秦东 +@ 时间: 2024-12-11 13:40:45 +@ 功能: 语音消息 +*/ +func (s *SendMessage) VoiceMethod() (msgMap map[string]interface{}, err error) { + msgInfo := publicmethod.MapOut[string]() //模版输出 + if s.VoiceMsg.MediaId != "" { + msgInfo["media_id"] = s.VoiceMsg.MediaId + } else { + err = errors.New("没有要发送的语音文件id!不可发送信息") + return + } + msgMap = msgInfo + return +} + +/* +* +@ 作者: 秦东 +@ 时间: 2024-12-11 13:40:45 +@ 功能: 视频消息 +*/ +func (s *SendMessage) VideoMethod() (msgMap map[string]interface{}, err error) { + msgInfo := publicmethod.MapOut[string]() //模版输出 + if s.VideoMsg.MediaId != "" { + msgInfo["media_id"] = s.VideoMsg.MediaId + } else { + err = errors.New("没有要发送的视频媒体文件id!不可发送信息") + return + } + if s.VideoMsg.Title != "" { + msgInfo["title"] = s.VideoMsg.Title + } + if s.VideoMsg.Description != "" { + msgInfo["description"] = s.VideoMsg.Description + } + msgMap = msgInfo + return +} + +/* +* +@ 作者: 秦东 +@ 时间: 2024-12-11 13:40:45 +@ 功能: 文件消息 +*/ +func (s *SendMessage) FileMethod() (msgMap map[string]interface{}, err error) { + msgInfo := publicmethod.MapOut[string]() //模版输出 + if s.FileMsg.MediaId != "" { + msgInfo["media_id"] = s.FileMsg.MediaId + } else { + err = errors.New("没有要发送的文件id!不可发送信息") + return + } + msgMap = msgInfo + return +} + +/* +* +@ 作者: 秦东 +@ 时间: 2024-12-11 13:40:45 +@ 功能: 文本卡片消息 +*/ +func (s *SendMessage) TextcardMethod() (msgMap map[string]interface{}, err error) { + msgInfo := publicmethod.MapOut[string]() //模版输出 + if s.Textcard.Title != "" && s.Textcard.Description != "" && s.Textcard.Url != "" { + msgInfo["title"] = s.Textcard.Title + msgInfo["description"] = s.Textcard.Description + msgInfo["url"] = s.Textcard.Url + } else { + err = errors.New("发送信息不全!不可发送信息") + return + } + if s.Textcard.Btntxt != "" { + msgInfo["btntxt"] = s.Textcard.Btntxt + } + msgMap = msgInfo + return +} + +/* +* +@ 作者: 秦东 +@ 时间: 2024-12-11 13:40:45 +@ 功能: 图文消息 +*/ +func (s *SendMessage) NewsMethod() (msgMap map[string]interface{}, err error) { + msgInfo := publicmethod.MapOut[string]() //模版输出 + if len(s.NewsMsg.Articles) > 0 { + var artList []interface{} + for _, v := range s.NewsMsg.Articles { + artMap, artIsOk := v.Articles_list() + if artIsOk { + artList = append(artList, artMap) + } + } + if len(artList) > 0 { + msgInfo["articles"] = artList + } else { + err = errors.New("没有要发送得信息!不可发送信息") + return + } + } else { + err = errors.New("没有要发送得信息!不可发送信息") + return + } + msgMap = msgInfo + return +} + +/* +* +@ 作者: 秦东 +@ 时间: 2024-12-11 13:40:45 +@ 功能: 图文消息(mpnews) +*/ +func (s *SendMessage) MpnewsMethod() (msgMap map[string]interface{}, err error) { + msgInfo := publicmethod.MapOut[string]() //模版输出 + if len(s.MpnewsMsg.Articles) > 0 { + var newList []interface{} + for _, v := range s.MpnewsMsg.Articles { + newCont, newIsTrue := v.Articles_list_mpn() + if newIsTrue { + newList = append(newList, newCont) + } + } + if len(newList) > 0 { + msgInfo["articles"] = newList + } else { + err = errors.New("没有要发送得信息!不可发送信息") + return + } + } else { + err = errors.New("没有要发送得信息!不可发送信息") + return + } + msgMap = msgInfo + return +} + +/* +* +@ 作者: 秦东 +@ 时间: 2024-12-11 13:40:45 +@ 功能: markdown消息 +*/ +func (s *SendMessage) MarkdownMethod() (msgMap map[string]interface{}, err error) { + msgInfo := publicmethod.MapOut[string]() //模版输出 + if s.MarkdownMsg.Content != "" { + msgInfo["content"] = s.MarkdownMsg.Content + } else { + err = errors.New("没有要发送的markdown消息内容!不可发送信息") + return + } + msgMap = msgInfo + return +} + +/* +* +@ 作者: 秦东 +@ 时间: 2024-12-11 13:40:45 +@ 功能: 小程序通知消息 +*/ +func (s *SendMessage) MiniprogramNoticeMethod() (msgMap map[string]interface{}, err error) { + msgInfo := publicmethod.MapOut[string]() //模版输出 + if s.MiniprogramMoticeMsg.Appid != "" && s.MiniprogramMoticeMsg.Title != "" { + msgInfo := publicmethod.MapOut[string]() //模版输出 + msgInfo["appid"] = s.MiniprogramMoticeMsg.Appid + msgInfo["title"] = s.MiniprogramMoticeMsg.Title + msgInfo["emphasis_first_item"] = s.MiniprogramMoticeMsg.EmphasisFirstItem + if s.MiniprogramMoticeMsg.Page != "" { + msgInfo["page"] = s.MiniprogramMoticeMsg.Page + } + if s.MiniprogramMoticeMsg.Description != "" { + msgInfo["description"] = s.MiniprogramMoticeMsg.Description + } + if len(s.MiniprogramMoticeMsg.ContentItem) > 0 { + var cotList []interface{} + for _, v := range s.MiniprogramMoticeMsg.ContentItem { + cotInFo, cotIsTrue := v.Content_item() + if cotIsTrue { + cotList = append(cotList, cotInFo) + } + } + if len(cotList) > 0 { + msgInfo["content_item"] = cotList + } + } + } else { + err = errors.New("发送信息不全!不可发送信息") + return + } + msgMap = msgInfo + return +} diff --git a/api/version1/workWechat/sendMsg.go b/api/version1/workWechat/sendMsg.go new file mode 100644 index 0000000..427267e --- /dev/null +++ b/api/version1/workWechat/sendMsg.go @@ -0,0 +1,278 @@ +package workWechat + +import ( + "appPlatform/overall" + "appPlatform/overall/publicmethod" + "encoding/json" + "errors" + "fmt" + "strconv" + "time" + + "github.com/gin-gonic/gin" +) + +/* +* +@ 作者: 秦东 +@ 时间: 2024-12-09 16:37:10 +@ 功能: 发送消息 +@ 参数 + + # + +@ 返回值 + + # + +@ 方法原型 + + # +*/ +func (a *ApiMethod) SendMsg(c *gin.Context) { + var requestData SendMessage + c.ShouldBindJSON(&requestData) + // var sendText SendMessage + // sendText.Touser = "KaiXinGuo" + // sendText.Msgtype = "template_card" + // sendText.Agentid = 1000108 + // sendText.TemplateCard.TaskId = strconv.FormatInt(publicmethod.GetUUid(1), 10) + // sendText.TemplateCard.CardType = requestData.Name + // sendText.TemplateCard.TextTemplate.CardAction.Type = 1 + // sendText.TemplateCard.TextTemplate.CardAction.Url = "https://www.baidu.com" + err := requestData.SendMsg("stzl", 1) + fmt.Printf("sendText: %v\n\n\n", err) + if err != nil { + publicmethod.Result(1, requestData, c, "消息发送失败!") + } else { + publicmethod.Result(0, err, c) + } + +} + +func (m *SendMapMsg) SendMsg(systemApp string, calss int) (err error) { + sendUrl, _, err := GetSendMsgTokenUrl(systemApp, 1) + if err != nil { + return + } + sendMsgData, _ := json.Marshal(m.Send) + callBackByte := publicmethod.CurlPostJosn(sendUrl, sendMsgData) + var callBackMap interface{} + err = json.Unmarshal(callBackByte, &callBackMap) + fmt.Printf("jsonCont==>%v\n\n\n\n", string(sendMsgData)) + resendIsRun := false + if val, isOk := callBackMap.(map[string]interface{}); isOk { + if mapVal, isOk := val["errcode"]; isOk { + errcode, err := publicmethod.StringToInt64(mapVal) + if err != nil { + resendIsRun = true + } else { + // fmt.Printf("errcode==>%v\n\n\n\n", errcode) + if errcode == 0 { + resendIsRun = false + } else { + resendIsRun = true + } + } + } else { + resendIsRun = true + } + } else { + resendIsRun = true + } + if resendIsRun { + m.Number++ + err = m.ResendSendMsg(systemApp, calss) + // fmt.Scanln() + // m.SendMsg(systemApp, calss) + return + } + // fmt.Printf("callBackMap==>%v\n\n-->%T\n\n", callBackMap, callBackMap) + // fmt.Printf("token==>%v\n\n\n\n", token) + return +} + +/* +* +@ 作者: 秦东 +@ 时间: 2024-12-11 09:56:18 +@ 功能: 为得到正确信息是重复发送请求,最多发送三次 +*/ +func (m *SendMapMsg) ResendSendMsg(systemApp string, calss int) (err error) { + fmt.Printf("执行第几次--->%v\n", m.Number) + if m.Number <= 3 { + go func() { + time.Sleep(time.Duration(5) * time.Second) + // fmt.Println("这条信息将在几秒后显示。") + err = m.SendMsg(systemApp, calss) + }() + } else { + fmt.Printf("超过最大补发次数!消息作废!--->%v\n", m.Number) + err = errors.New("超过最大补发次数!消息作废!") + } + return +} + +/* +* +@ 作者: 秦东 +@ 时间: 2024-12-10 16:28:43 +@ 功能: 企业微信发送应用消息URL组装 +@ 参数 + + #systemApp 系统 + #class 类型 1:发送;2:更新;3:撤回 + +@ 返回值 + + #sendUrl 发送应用消息URL + #token token + #err 系统信息 + +@ 方法原型 + + # +*/ +func GetSendMsgTokenUrl(systemApp string, class int) (sendUrl string, token string, err error) { + //获取token + token, err = GainWechatToken(systemApp, "sendWorkWechat", 2) + if err != nil { + return + } + switch class { + case 2: + sendUrl = fmt.Sprintf("https://qyapi.weixin.qq.com/cgi-bin/message/update_template_card?access_token=%v", token) + case 3: + sendUrl = fmt.Sprintf("https://qyapi.weixin.qq.com/cgi-bin/message/recall?access_token=%v", token) + default: + sendUrl = fmt.Sprintf("https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=%v", token) + } + return +} + +/* +* +@ 作者: 秦东 +@ 时间: 2024-12-11 10:19:18 +@ 功能: +*/ +func (s *SendMessage) MsgInit() { + if s.Msgtype == "" { + s.Msgtype = "template_card" + } + if s.Agentid == 0 || s.Agentid == 1 { + s.Agentid, _ = strconv.Atoi(overall.CONSTANT_CONFIG.ShuTongZhiLian.Agentid) + } + if s.DuplicateCheckInterval == 0 { + s.DuplicateCheckInterval = 1800 + } + if s.TemplateCard.CardType == "" { + s.TemplateCard.CardType = "text_notice" + } + if s.TemplateCard.Source.IconUrl == "" { + s.TemplateCard.Source.IconUrl = "https://docu.hxgk.group/images/2022_01/3f7a1120a559e9bee3991b85eb34d103.png" + } + if s.TemplateCard.Source.Desc == "" { + s.TemplateCard.Source.Desc = "数通智联化工云平台" + } else { + s.TemplateCard.Source.Desc = fmt.Sprintf("数通智联化工云平台 - %v", s.TemplateCard.Source.Desc) + } + if s.TemplateCard.Source.DescColor == 0 { + s.TemplateCard.Source.DescColor = 1 + } +} + +/* +* +@ 作者: 秦东 +@ 时间: 2024-12-10 13:55:14 +@ 功能: 通用发送消息 +@ 参数 + + # + +@ 返回值 + + # + +@ 方法原型 + + # +*/ +func (s *SendMessage) SendMsg(systemApp string, class int) (err error) { + s.MsgInit() + topMapAry, err := s.MsgCommon.MsgCommon() + if err != nil { + return + } + msgCommonFooter := s.MsgCommonFooter.MsgCommonFooter(s.Msgtype) + var mapAry SendMapMsg + mapAry.Send = topMapAry + for i, v := range msgCommonFooter { + mapAry.Send[i] = v + } + + switch s.Msgtype { + case "text": + mapAry.Send["text"], err = s.TextMethod() + if err != nil { + return + } + case "image": + mapAry.Send["image"], err = s.ImageMethod() + if err != nil { + return + } + case "voice": + mapAry.Send["voice"], err = s.VoiceMethod() + if err != nil { + return + } + case "video": //视频消息 + mapAry.Send["video"], err = s.VideoMethod() + if err != nil { + return + } + case "file": //文件消息 + mapAry.Send["file"], err = s.FileMethod() + if err != nil { + return + } + case "textcard": //文本卡片消息 + mapAry.Send["textcard"], err = s.TextcardMethod() + if err != nil { + return + } + case "news": //图文消息 + mapAry.Send["news"], err = s.NewsMethod() + if err != nil { + return + } + case "mpnews": //图文消息(mpnews) + mapAry.Send["mpnews"], err = s.MpnewsMethod() + if err != nil { + return + } + case "markdown": //markdown消息 + mapAry.Send["markdown"], err = s.MarkdownMethod() + if err != nil { + return + } + case "miniprogram_notice": //小程序通知消息 + mapAry.Send["content_item"], err = s.MiniprogramNoticeMethod() + if err != nil { + return + } + case "template_card": + mapAry.Send["template_card"], err = s.TemplateCardMethod() + if err != nil { + return + } + default: + err = errors.New("没有消息类型!不可发送信息") + return + } + + // mapAry.Send["template_card"] = templateCard + return mapAry.SendMsg(systemApp, class) +} diff --git a/api/version1/workWechat/workMsgType.go b/api/version1/workWechat/workMsgType.go new file mode 100644 index 0000000..75e3ed3 --- /dev/null +++ b/api/version1/workWechat/workMsgType.go @@ -0,0 +1,421 @@ +package workWechat + +//公用头部 +//touser&toparty&totag 这三个参数进行三选一。必须有一个存在! +type MsgCommon struct { + Touser string `json:"touser"` //成员ID列表(消息接收者,多个接收者用‘|’分隔,最多支持1000个)。特殊情况:指定为@all,则向关注该企业应用的全部成员发送 + Toparty string `json:"toparty"` //部门ID列表,多个接收者用‘|’分隔,最多支持100个。当touser为@all时忽略本参数 + Totag string `json:"totag"` //标签ID列表,多个接收者用‘|’分隔,最多支持100个。当touser为@all时忽略本参数 + Msgtype string `json:"msgtype"` //消息类型,此时固定为:template_card + Agentid int `json:"agentid"` //企业应用的id,整型。企业内部开发,可在应用的设置页面查看;第三方服务商,可通过接口 获取企业授权信息 获取该参数值 +} + +//公用底部 +//此部分参数为非必填参数 +type MsgCommonFooter struct { + Safe int `json:"safe"` //表示是否开启id转译,0表示否,1表示是,默认0 + EnableIdTrans int `json:"enable_id_trans"` //表示是否开启id转译,0表示否,1表示是,默认0 + EnableDuplicateCheck int `json:"enable_duplicate_check"` //表示是否开启重复消息检查,0表示否,1表示是,默认0 + DuplicateCheckInterval int `json:"duplicate_check_interval"` //表示是否重复消息检查的时间间隔,默认1800s,最大不超过4小时 +} + +/** +@ 作者: 秦东 +@ 时间: 2024-12-09 15:23:36 +@ 功能: 消息卡片相同的地方 +*/ +type TemplateCardCommon struct { + CardType string `json:"card_type"` //模板卡片类型,文本通知型卡片填写 "text_notice" + Source Source `json:"source"` //卡片来源样式信息,不需要来源样式可不填写 + ActionMenu Action_menu `json:"action_menu"` //卡片右上角更多操作按钮 + TaskId string `json:"task_id"` //任务id,同一个应用任务id不能重复,只能由数字、字母和“_-@”组成,最长128字节,填了action_menu字段的话本字段必填 + MainTitle Main_title `json:"main_title"` //一级标题 + QuoteArea Quote_area `json:"quote_area"` //引用文献样式 + HorizontalContentList []Horizontal_content_list `json:"horizontal_content_list"` //二级标题+文本列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过6 + JumpList []Jump_list `json:"jump_list"` //跳转指引样式的列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过3 + CardAction Card_action `json:"card_action"` //整体卡片的点击跳转事件,news_notice必填本字段 +} + +//卡片来源样式信息,不需要来源样式可不填写 +type Source struct { + IconUrl string `json:"icon_url"` //来源图片的url,来源图片的尺寸建议为72*72 + Desc string `json:"desc"` //来源图片的描述,建议不超过20个字, + DescColor int `json:"desc_color"` //来源文字的颜色,目前支持:0(默认) 灰色,1 黑色,2 红色,3 绿色 +} + +//操作列表,列表长度取值范围为 [1, 3] +type Action_list struct { + Text string `json:"text"` //操作的描述文案 + Key string `json:"key"` //操作key值,用户点击后,会产生回调事件将本参数作为EventKey返回,回调事件会带上该key值,最长支持1024字节,不可重复 +} + +//卡片右上角更多操作按钮 +type Action_menu struct { + Desc string `json:"desc"` //更多操作界面的描述 + ActionList []Action_list `json:"action_list"` //操作列表,列表长度取值范围为 [1, 3] +} + +//一级标题 +type Main_title struct { + Title string `json:"title"` //一级标题,建议不超过36个字,文本通知型卡片本字段非必填,但不可本字段和sub_title_text都不填 + Desc string `json:"desc"` //标题辅助信息,建议不超过44个字 +} + +//引用文献样式 +type Quote_area struct { + Type int `json:"type"` //引用文献样式区域点击事件,0或不填代表没有点击事件,1 代表跳转url,2 代表跳转小程序 + Url string `json:"url"` //点击跳转的url,quote_area.type是1时必填 + Appid string `json:"appid"` //点击跳转的小程序的appid,必须是与当前应用关联的小程序,quote_area.type是2时必填 + Pagepath string `json:"pagepath"` //点击跳转的小程序的pagepath,quote_area.type是2时选填 + Title string `json:"title"` //引用文献样式的标题 + Quote_text string `json:"quote_text"` //引用文献样式的引用文案 +} + +//关键数据样式 +type Emphasis_content struct { + Title string `json:"title"` //关键数据样式的数据内容,建议不超过14个字 + Desc string `json:"desc"` //关键数据样式的数据描述内容,建议不超过22个字 +} + +//二级标题+文本列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过6 +type Horizontal_content_list struct { + Type int `json:"type"` //链接类型,0或不填代表不是链接,1 代表跳转url,2 代表下载附件,3 代表点击跳转成员详情 + Keyname string `json:"keyname"` //二级标题,建议不超过5个字 + Value string `json:"value"` //二级文本,如果horizontal_content_list.type是2,该字段代表文件名称(要包含文件类型),建议不超过30个字, + Url string `json:"url"` //链接跳转的url,horizontal_content_list.type是1时必填 + MediaId string `json:"media_id"` //附件的media_id,horizontal_content_list.type是2时必填 + UserId string `json:"userid"` //成员详情的userid,horizontal_content_list.type是3时必填 +} + +//跳转指引样式的列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过3 +type Jump_list struct { + Type int `json:"type"` //跳转链接类型,0或不填代表不是链接,1 代表跳转url,2 代表跳转小程序 + Title string `json:"title"` //跳转链接样式的文案内容,建议不超过18个字 + Url string `json:"url"` //跳转链接的url,jump_list.type是1时必填 + Appid string `json:"appid"` //跳转链接的小程序的appid,必须是与当前应用关联的小程序,jump_list.type是2时必填 + Pagepath string `json:"pagepath"` //跳转链接的小程序的pagepath,jump_list.type是2时选填 +} + +//整体卡片的点击跳转事件,text_notice必填本字段 +type Card_action struct { + Type int `json:"type"` //跳转事件类型,1 代表跳转url,2 代表打开小程序。text_notice卡片模版中该字段取值范围为[1,2] + Url string `json:"url"` //跳转事件的url,card_action.type是1时必填 + Appid string `json:"appid"` //跳转事件的小程序的appid,必须是与当前应用关联的小程序,card_action.type是2时必填 + Pagepath string `json:"pagepath"` //跳转事件的小程序的pagepath,card_action.type是2时选填 +} + +/** +@ 作者: 秦东 +@ 时间: 2024-12-09 15:55:58 +@ 功能: 发送文本通知型消息 +*/ +type SendTextMessage struct { + MsgCommon + TemplateCard TextTemplateCard `json:"template_card"` //消息类型 + MsgCommonFooter +} + +//文本发射体 +type TextTemplateCard struct { + TemplateCardCommon + Emphasis_content + Sub_title_text string `json:"sub_title_text"` +} + +/** +@ 作者: 秦东 +@ 时间: 2024-12-09 16:08:53 +@ 功能: 发送图片通知消息 +*/ +type SendPictureMessage struct { + MsgCommon + TemplateCard TextTemplateCard `json:"template_card"` //消息类型 + MsgCommonFooter +} + +//图片发射体 +type PictureTemplateCard struct { + TemplateCardCommon + ImageTextArea Image_text_area `json:"image_text_area"` //左图右文样式,news_notice类型的卡片,card_image和image_text_area两者必填一个字段,不可都不填 + CardImage Card_image `json:"card_image"` //图片样式,news_notice类型的卡片,card_image和image_text_area两者必填一个字段,不可都不填 + VerticalContentList Vertical_content_list `json:"vertical_content_list"` //卡片二级垂直内容,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过4 + Sub_title_text string `json:"sub_title_text"` +} + +//卡片二级垂直内容,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过4 +type Vertical_content_list struct { + Title string `json:"title"` //卡片二级标题,建议不超过38个字 + Desc string `json:"desc"` //二级普通文本,建议不超过160个字 +} + +//图片样式,news_notice类型的卡片,card_image和image_text_area两者必填一个字段,不可都不填 +type Card_image struct { + Url string `json:"url"` //图片的url + AspectRatio float32 `json:"aspect_ratio"` //图片的宽高比,宽高比要小于2.25,大于1.3,不填该参数默认1.3 +} + +//左图右文样式,news_notice类型的卡片,card_image和image_text_area两者必填一个字段,不可都不填 +type Image_text_area struct { + Type int `json:"type"` //左图右文样式区域点击事件,0或不填代表没有点击事件,1 代表跳转url,2 代表跳转小程序 + Url string `json:"url"` //点击跳转的url,image_text_area.type是1时必填 + Title string `json:"title"` //左图右文样式的标题 + Desc string `json:"desc"` //左图右文样式的描述 + ImageUrl string `json:"image_url"` //左图右文样式的图片url + Appid string `json:"appid"` //点击跳转的小程序的appid,必须是与当前应用关联的小程序,image_text_area.type是2时必填 + Pagepath string `json:"pagepath"` //点击跳转的小程序的pagepath,image_text_area.type是2时选填 +} + +//下拉式的选择器 +type Button_selection struct { + QuestionKey string `json:"question_key"` //下拉式的选择器的key,用户提交选项后,会产生回调事件,回调事件会带上该key值表示该题,最长支持1024字节 + Title string `json:"title"` //下拉式的选择器左边的标题 + SelectedId string `json:"selected_id"` //默认选定的id,不填或错填默认第一个 + OptionList []Option_list `json:"option_list"` //选项列表,下拉选项不超过 10 个,最少1个 +} + +//选项列表,下拉选项不超过 10 个,最少1个 +type Option_list struct { + Id string `json:"id"` //下拉式的选择器选项的id,用户提交后,会产生回调事件,回调事件会带上该id值表示该选项,最长支持128字节,不可重复 + Text string `json:"text"` //下拉式的选择器选项的文案,建议不超过16个字 +} + +//按钮列表,列表长度不超过6 +type Button_list struct { + Type int `json:"type"` //按钮点击事件类型,0 或不填代表回调点击事件,1 代表跳转url + Text string `json:"text"` //按钮文案,建议不超过10个字 + Style int `json:"style"` //按钮样式,目前可填1~4,不填或错填默认1 + Key string `json:"key"` //按钮key值,用户点击后,会产生回调事件将本参数作为EventKey返回,回调事件会带上该key值,最长支持1024字节,不可重复,button_list.type是0时必填 + Url string `json:"url"` //跳转事件的url,button_list.type是1时必填 +} + +// +type Checkbox struct { + Mode int `json:"mode"` //选择题模式,单选:0,多选:1,不填默认0 + QuestionKey string `json:"question_key"` //选择题key值,用户提交选项后,会产生回调事件,回调事件会带上该key值表示该题,最长支持1024字节 + OptionList []OptionList `json:"option_list"` //选项list,选项个数不超过 20 个,最少1个 +} + +//选项list,选项个数不超过 20 个,最少1个 +type OptionList struct { + Id string `json:"id"` //选项id,用户提交选项后,会产生回调事件,回调事件会带上该id值表示该选项,最长支持128字节,不可重复 + Text string `json:"text"` //选项文案描述,建议不超过17个字 + IsChecked bool `json:"is_checked"` //该选项是否要默认选中 +} + +//提交按钮样式 +type Submit_button struct { + Text string `json:"text"` //按钮文案,建议不超过10个字,不填默认为提交 + Key string `json:"key"` //提交按钮的key,会产生回调事件将本参数作为EventKey返回,最长支持1024字节 +} + +//发送消息接口 +type SendMsgInterface interface { + SendTextMessage | SendPictureMessage +} + +//Map类型发送消息 +type SendMapMsg struct { + Send map[string]interface{} + Number int +} + +//文本消息 +type Text_msg struct { + Content string `json:"content"` //消息内容,最长不超过2048个字节,超过将截断 +} + +//图片消息 +type Image_msg struct { + MediaId string `json:"media_id"` //图片媒体文件id,可以调用上传临时素材接口获取 +} + +//视频消息 +type Video_msg struct { + Image_msg //视频媒体文件id,可以调用上传临时素材接口获取 + Title string `json:"title"` //视频消息的标题,不超过128个字节,超过会自动截断 + Description string `json:"description"` //视频消息的描述,不超过512个字节,超过会自动截断 +} + +//音频消息 +type Voice_msg struct { + Image_msg +} + +//文件消息 +type File_msg struct { + Image_msg //文件id,可以调用上传临时素材接口获取 +} + +//文本卡片消息 +type Textcard_msg struct { + Title string `json:"title"` //标题,不超过128个字符,超过会自动截断(支持id转译) + Description string `json:"description"` //描述,不超过512个字符,超过会自动截断(支持id转译) + Url string `json:"url"` //点击后跳转的链接。最长2048字节,请确保包含了协议头(http/https) + Btntxt string `json:"btntxt"` //按钮文字。 默认为“详情”, 不超过4个文字,超过自动截断。 +} + +//图文消息 +type News_msg struct { + Articles []ArticlesList `json:"articles"` //图文消息,一个图文消息支持1到8条图文 +} + +//图文消息, +type ArticlesList struct { + Title string `json:"title"` //标题,不超过128个字节,超过会自动截断(支持id转译) + Description string `json:"description"` //描述,不超过512个字节,超过会自动截断(支持id转译) + Url string `json:"url"` //点击后跳转的链接。 最长2048字节,请确保包含了协议头(http/https),小程序或者url必须填写一个 + PicUrl string `json:"picurl"` //图文消息的图片链接,最长2048字节,支持JPG、PNG格式,较好的效果为大图 1068*455,小图150*150。 + AppId string `json:"appid"` //小程序appid,必须是与当前应用关联的小程序,appid和pagepath必须同时填写,填写后会忽略url字段 + Pagepath string `json:"pagepath"` //点击消息卡片后的小程序页面,最长128字节,仅限本小程序内的页面。appid和pagepath必须同时填写,填写后会忽略url字段 +} + +//图文消息(mpnews) +type Mpnews_msg struct { + Articles []ArticlesListMpn `json:"articles"` //图文消息,一个图文消息支持1到8条图文 +} +type ArticlesListMpn struct { + Title string `json:"title"` //标题,不超过128个字节,超过会自动截断(支持id转译) + ThumbMediaId string `json:"thumb_media_id"` //图文消息缩略图的media_id, 可以通过素材管理接口获得。此处thumb_media_id即上传接口返回的media_id + Author string `json:"author"` //图文消息的作者,不超过64个字节 + ContentSourceUrl string `json:"content_source_url"` //图文消息点击“阅读原文”之后的页面链接 + Content string `json:"content"` //图文消息的内容,支持html标签,不超过666 K个字节(支持id转译) + Digest string `json:"digest"` //图文消息的描述,不超过512个字节,超过会自动截断(支持id转译) +} + +//markdown消息 +type Markdown_msg struct { + Content string `json:"content"` //markdown内容,最长不超过2048个字节,必须是utf8编码 +} + +//小程序通知消息 +type Miniprogram_notice_msg struct { + Appid string `json:"appid"` //小程序appid,必须是与当前应用关联的小程序 + Page string `json:"page"` //点击消息卡片后的小程序页面,最长1024个字节,仅限本小程序内的页面。该字段不填则消息点击后不跳转。 + Title string `json:"title"` //消息标题,长度限制4-12个汉字(支持id转译) + Description string `json:"description"` //消息描述,长度限制4-12个汉字(支持id转译) + EmphasisFirstItem bool `json:"emphasis_first_item"` //是否放大第一个content_item + ContentItem []Content_item `json:"content_item"` //消息内容键值对,最多允许10个item +} + +//消息内容键值对,最多允许10个item +type Content_item struct { + Key string `json:"key"` //长度10个汉字以内 + Value string `json:"value"` //长度30个汉字以内(支持id转译)key和value两个字段同时为空时,该键值对将被忽略 +} + +/** +@ 作者: 秦东 +@ 时间: 2024-12-10 13:05:27 +@ 功能: 发送消息 +*/ +type SendMessage struct { + MsgCommon + + TextMsg Text_msg `json:"text"` //文本消息 + ImageMsg Image_msg `json:"image"` //图片消息 + VoiceMsg Voice_msg `json:"voice"` //语音消息 + VideoMsg Video_msg `json:"video"` //视频消息 + FileMsg File_msg `json:"file"` //文件消息 + Textcard Textcard_msg `json:"textcard"` //文本卡片消息 + NewsMsg News_msg `json:"news"` //图文消息 + MpnewsMsg Mpnews_msg `json:"mpnews"` //图文消息(mpnews) + MarkdownMsg Markdown_msg `json:"markdown"` //markdown消息 + MiniprogramMoticeMsg Miniprogram_notice_msg `json:"miniprogram_notice"` //小程序通知消息 + + TemplateCard TemplateCard `json:"template_card"` //模版卡片消息类型 + MsgCommonFooter +} + +//发送消息发射体 +type TemplateCard struct { + TemplateCommon + + TextTemplate //专属文本消息 + ImageTemplate //专属图文消息 + ButtonTemplate //专属按钮消息 + VoteTemplate //专属投票消息 + MultipleTemplate //专属多选消息 +} + +/** +@ 作者: 秦东 +@ 时间: 2024-12-09 15:23:36 +@ 功能: 所有消息卡片相同的地方 +*/ +type TemplateCommon struct { + CardType string `json:"card_type"` //模板卡片类型,文本通知型卡片填写 "text_notice" + Source Source `json:"source"` //卡片来源样式信息,不需要来源样式可不填写 + TaskId string `json:"task_id"` //任务id,同一个应用任务id不能重复,只能由数字、字母和“_-@”组成,最长128字节,填了action_menu字段的话本字段必填 + MainTitle Main_title `json:"main_title"` //一级标题 +} + +/** +@ 作者: 秦东 +@ 时间: 2024-12-10 13:48:50 +@ 功能: 专属多选消息 +*/ +type MultipleTemplate struct { + SelectList []Button_selection `json:"select_list"` //下拉式的选择器列表,multiple_interaction类型的卡片该字段不可为空,一个消息最多支持 3 个选择器 +} + +/** +@ 作者: 秦东 +@ 时间: 2024-12-10 13:38:16 +@ 功能: 专属投票消息 +*/ +type VoteTemplate struct { + CheckBox Checkbox `json:"checkbox"` //选择题样式 + SubmitButton Submit_button `json:"submit_button"` //提交按钮样式 +} + +/** +@ 作者: 秦东 +@ 时间: 2024-12-10 13:21:42 +@ 功能: 专属按钮消息 +*/ +type ButtonTemplate struct { + // ActionMenu Action_menu `json:"action_menu"` //卡片右上角更多操作按钮 + // QuoteArea Quote_area `json:"quote_area"` //引用文献样式 + // SubTitleText string `json:"sub_title_text"` //二级普通文本,建议不超过160个字,(支持id转译) + // HorizontalContentList []Horizontal_content_list `json:"horizontal_content_list"` //二级标题+文本列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过6 + // CardAction Card_action `json:"card_action"` //整体卡片的点击跳转事件,news_notice必填本字段 + ButtonSelection Button_selection `json:"button_selection"` //下拉式的选择器 + ButtonList []Button_list `json:"button_list"` //按钮列表,列表长度不超过6 +} + +/** +@ 作者: 秦东 +@ 时间: 2024-12-10 13:13:24 +@ 功能: 专属文本消息 +*/ +type TextTemplate struct { + ActionMenu Action_menu `json:"action_menu"` //卡片右上角更多操作按钮 + QuoteArea Quote_area `json:"quote_area"` //引用文献样式 + EmphasisContent Emphasis_content `json:"emphasis_content"` //关键数据样式 + SubTitleText string `json:"sub_title_text"` //二级普通文本,建议不超过160个字,(支持id转译) + HorizontalContentList []Horizontal_content_list `json:"horizontal_content_list"` //二级标题+文本列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过6 + JumpList []Jump_list `json:"jump_list"` //跳转指引样式的列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过3 + CardAction Card_action `json:"card_action"` //整体卡片的点击跳转事件,news_notice必填本字段 +} + +/** +@ 作者: 秦东 +@ 时间: 2024-12-10 13:18:26 +@ 功能: 专属图文消息 +*/ +type ImageTemplate struct { + // ActionMenu Action_menu `json:"action_menu"` //卡片右上角更多操作按钮 + // QuoteArea Quote_area `json:"quote_area"` //引用文献样式 + ImageTextArea Image_text_area `json:"image_text_area"` //左图右文样式,news_notice类型的卡片,card_image和image_text_area两者必填一个字段,不可都不填 + CardImage Card_image `json:"card_image"` //图片样式,news_notice类型的卡片,card_image和image_text_area两者必填一个字段,不可都不填 + VerticalContentList []Vertical_content_list `json:"vertical_content_list"` //卡片二级垂直内容,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过4 + // HorizontalContentList []Horizontal_content_list `json:"horizontal_content_list"` //二级标题+文本列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过6 + // JumpList []Jump_list `json:"jump_list"` //跳转指引样式的列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过3 + // CardAction Card_action `json:"card_action"` //整体卡片的点击跳转事件,news_notice必填本字段 +} + +//判断是否执行补发信息 +type ResendInfo struct { + Number int + IsRun bool +} diff --git a/apirouter/v1/customerformrouter/router.go b/apirouter/v1/customerformrouter/router.go index 70cf0cb..876c506 100644 --- a/apirouter/v1/customerformrouter/router.go +++ b/apirouter/v1/customerformrouter/router.go @@ -98,5 +98,7 @@ func (a *ApiRouter) RouterGroupPc(router *gin.RouterGroup) { appApiRouter.POST("getAppList", methodAppHand.GetAppList) //根据分组获取App列表 appApiRouter.POST("getGroupAndApp", methodAppHand.GetGroupAndApp) //获取分组及App + + appApiRouter.POST("gainMenuGroupForm", methodAppHand.GainMenuGroupForm) //按菜单顶级分组和表单 } } diff --git a/apirouter/v1/taskrouter/workFlow.go b/apirouter/v1/taskrouter/workFlow.go new file mode 100644 index 0000000..93f4623 --- /dev/null +++ b/apirouter/v1/taskrouter/workFlow.go @@ -0,0 +1,20 @@ +package taskrouter + +import ( + "appPlatform/api/version1" + + "github.com/gin-gonic/gin" +) + +func (a *ApiRouter) RouterGroupFlow(router *gin.RouterGroup) { + apiRouter := router.Group("flow") + var workFlowRouter = version1.AppApiEntry.WorkFlowApi + { + apiRouter.GET("", workFlowRouter.Index) //入口 + apiRouter.POST("", workFlowRouter.Index) + + apiRouter.POST("startProcess", workFlowRouter.StartProcess) //启动流程 + + apiRouter.POST("runTaskFlow", workFlowRouter.RunTaskWorkFlow) //启动流程 + } +} diff --git a/apirouter/v1/workWechatRouter/router.go b/apirouter/v1/workWechatRouter/router.go index 756b159..8494c4c 100644 --- a/apirouter/v1/workWechatRouter/router.go +++ b/apirouter/v1/workWechatRouter/router.go @@ -17,5 +17,6 @@ func (a *ApiRouter) RouterGroupPc(router *gin.RouterGroup) { apiRouter.POST("lookOnePeopleArchives", methodBinding.LookOnePeopleArchives) //获取人员信息单页(手机)查看权限 apiRouter.GET("obtainAuthorization", methodBinding.ObtainAuthorization) //构造企业微信网页授权code apiRouter.GET("wechatCallBack", methodBinding.WechatCallBack) //企业微信参数地址重定向回调 + apiRouter.POST("sendMsg", methodBinding.SendMsg) //向企业微信发送消息 } } diff --git a/initialization/route/initRoute.go b/initialization/route/initRoute.go index b505835..a84e9c7 100644 --- a/initialization/route/initRoute.go +++ b/initialization/route/initRoute.go @@ -103,6 +103,11 @@ func InitialRouter() *gin.Engine { //注册数学函数处理 mathsApiVerify := apirouter.RouterGroupEntry.MathsRouter mathsApiVerify.RouterGroup(VerifyIdentity) + //工作流执行相关 + workFlowRouterApi := apirouter.RouterGroupEntry.TaskRouter + { + workFlowRouterApi.RouterGroupFlow(VerifyIdentity) + } } //验证身份接口 无需鉴权Url(主要web端使用) VerifyIdentityWeb := router.Group("") diff --git a/models/customerForm/taskrecord.go b/models/customerForm/taskrecord.go index ec45374..b7d30f5 100644 --- a/models/customerForm/taskrecord.go +++ b/models/customerForm/taskrecord.go @@ -29,6 +29,7 @@ type TaskRecord struct { TableKey int64 `json:"tableKey" gorm:"column:tableKey;type:bigint(20) unsigned;default:0;not null;comment:归属自定义表"` AppKey int64 `json:"appKey" gorm:"column:appKey;type:bigint(20) unsigned;default:0;not null;comment:归属App(0:非app表单)"` RunFlowId int64 `json:"runFlowId" gorm:"column:runFlowId;type:bigint(20) unsigned;default:0;not null;comment:正在执行得流程"` + MsgId int64 `json:"msgId" gorm:"column:msgId;type:bigint(20) unsigned;default:0;not null;comment:发送消息得任务ID"` } func (TaskRecord *TaskRecord) TableName() string { diff --git a/overall/publicmethod/technique.go b/overall/publicmethod/technique.go index 237f394..67e3260 100644 --- a/overall/publicmethod/technique.go +++ b/overall/publicmethod/technique.go @@ -2570,6 +2570,29 @@ func (g *GetOrgAllParent) GetOrgSun(superior int64) { } } +/* +* +@ 作者: 秦东 +@ 时间: 2024-11-27 09:44:25 +@ 功能: 获取自定义App子菜单 +*/ +func (g *GetOrgAllParent) GetAppMenuSun(appKey, superior int64) { + var id []int64 + if superior == 0 { + return + } + err := overall.CONSTANT_DB_AppPlatform.Model(&modelAppPlatform.Appmenus{}).Select("`id`").Where("`state` = 1 AND `type` = 1 AND `isLock` = 2 AND `appkey` = ? AND `parent` = ?", appKey, superior).Find(&id).Error + if err != nil || len(id) < 1 { + return + } + for _, v := range id { + if !IsInTrue[int64](v, g.Id) { + g.Id = append(g.Id, v) + } + g.GetAppMenuSun(appKey, v) + } +} + /* * @ 作者: 秦东 @@ -2905,3 +2928,20 @@ func TeamidNameInt(val string) int64 { } return 0 } + +//取最大值 + +func GetMaxNum[T GenericityVariable](ary []T) T { + maxVal := ary[0] + if len(ary) == 0 { + return maxVal + } + + for i := 1; i < len(ary); i++ { + if maxVal < ary[i] { + maxVal = ary[i] + } + } + + return maxVal +}