Browse Source

Merge branch 'feature/eta_1.3.8'

hsun 1 year ago
parent
commit
81dccda57e

+ 52 - 0
controllers/business_conf.go

@@ -3,6 +3,7 @@ package controllers
 import (
 	"encoding/json"
 	"eta/eta_api/models"
+	"eta/eta_api/services"
 	"eta/eta_api/utils"
 	"fmt"
 	"html"
@@ -62,6 +63,9 @@ func (this *BusinessConfController) Save() {
 		confMap[c.ConfKey] = c
 	}
 
+	openApprove := ""
+	approveType := ""
+
 	// 根据配置类型取值
 	updates := make([]models.BusinessConfUpdate, 0)
 	for k, v := range req {
@@ -86,6 +90,18 @@ func (this *BusinessConfController) Save() {
 				ConfKey: k,
 				ConfVal: str,
 			})
+
+			// 取出审批参数
+			if k == models.BusinessConfIsReportApprove {
+				openApprove = str
+			}
+			if k == models.BusinessConfReportApproveType {
+				// 打开审批默认为内部审批方式
+				if openApprove == "true" && str == "" {
+					str = models.BusinessConfReportApproveTypeEta
+				}
+				approveType = str
+			}
 		case 2: // 数值
 			num, ok := v.(float64)
 			if !ok {
@@ -138,6 +154,42 @@ func (this *BusinessConfController) Save() {
 		}
 	}
 
+	// 校验报告审批是否可以切换
+	confOpenApprove := confMap[models.BusinessConfIsReportApprove]
+	confApproveType := confMap[models.BusinessConfReportApproveType]
+	if confOpenApprove != nil && confApproveType != nil {
+		// 仅校验有审批->无审批, 或是有审批->切换审批方式的情况
+		if openApprove == "false" && confOpenApprove.ConfVal == "true" || (openApprove == "true" && openApprove == confOpenApprove.ConfVal && confApproveType.ConfVal != approveType) {
+			ok, e := services.CheckCloseReportApproveConf()
+			if e != nil {
+				br.Msg = "保存失败"
+				br.ErrMsg = "校验是否可以关闭报告审批失败, Err: " + e.Error()
+				return
+			}
+			if !ok {
+				br.Msg = "当前有未走完流程的报告,请走完流程后再做变更"
+				return
+			}
+		}
+		// 审批设置切换对未发布/待提交报告状态的重置
+		needReset := false
+		changeType := ""
+		if openApprove == "false" && confOpenApprove.ConfVal == "true" {
+			needReset = true
+		}
+		if openApprove == "true" && confOpenApprove.ConfVal == "false" {
+			needReset = true
+			changeType = approveType
+		}
+		if openApprove == "true" && openApprove == confOpenApprove.ConfVal && confApproveType.ConfVal != approveType {
+			needReset = true
+			changeType = approveType
+		}
+		if needReset {
+			go services.ConfigChangeResetReportState(changeType)
+		}
+	}
+
 	if len(updates) > 0 {
 		if e = models.UpdateBusinessConfMulti(updates); e != nil {
 			br.Msg = "保存失败"

+ 24 - 0
controllers/classify.go

@@ -3,9 +3,11 @@ package controllers
 import (
 	"encoding/json"
 	"eta/eta_api/models"
+	"eta/eta_api/models/report_approve"
 	"eta/eta_api/models/system"
 	"eta/eta_api/services"
 	"eta/eta_api/utils"
+	"fmt"
 	"github.com/rdlucklib/rdluck_tools/paging"
 	"time"
 )
@@ -284,6 +286,28 @@ func (this *ClassifyController) CheckDeleteClassify() {
 			return
 		}
 	}
+
+	// 查询该分类是否关联了审批流
+	flowOb := new(report_approve.ReportApproveFlow)
+	flowCond := fmt.Sprintf(` AND (%s = ? OR %s = ?) AND (%s = ? OR %s = ?)`, report_approve.ReportApproveFlowCols.ReportType, report_approve.ReportApproveFlowCols.ReportType, report_approve.ReportApproveFlowCols.ClassifyFirstId, report_approve.ReportApproveFlowCols.ClassifySecondId)
+	flowPars := make([]interface{}, 0)
+	flowPars = append(flowPars, report_approve.FlowReportTypeChinese, report_approve.FlowReportTypeSmart, classifyId, classifyId)
+	flowCount, e := flowOb.GetCountByCondition(flowCond, flowPars)
+	if e != nil {
+		br.Msg = "检测失败"
+		br.ErrMsg = "获取关联审批流失败, Err: " + e.Error()
+		return
+	}
+	if flowCount > 0 {
+		resp.Code = 5
+		resp.Msg = "该分类关联审批流,不允许删除"
+		br.Data = resp
+		br.Ret = 200
+		br.Msg = resp.Msg
+		br.Success = true
+		return
+	}
+
 	resp.Code = 0
 	resp.Msg = "检测完成,可进行删除操作"
 	br.Ret = 200

+ 18 - 0
controllers/english_report/english_classify.go

@@ -4,6 +4,7 @@ import (
 	"encoding/json"
 	"eta/eta_api/controllers"
 	"eta/eta_api/models"
+	"eta/eta_api/models/report_approve"
 	"eta/eta_api/services"
 	"eta/eta_api/utils"
 	"fmt"
@@ -469,6 +470,23 @@ func (this *EnglishReportController) DelClassify() {
 		return
 	}
 
+	// 查询该分类是否关联了审批流
+	flowOb := new(report_approve.ReportApproveFlow)
+	flowCond := fmt.Sprintf(` AND %s = ? AND (%s = ? Or %s = ?)`, report_approve.ReportApproveFlowCols.ReportType, report_approve.ReportApproveFlowCols.ClassifyFirstId, report_approve.ReportApproveFlowCols.ClassifySecondId)
+	flowPars := make([]interface{}, 0)
+	flowPars = append(flowPars, report_approve.FlowReportTypeEnglish, classifyId, classifyId)
+	flowCount, e := flowOb.GetCountByCondition(flowCond, flowPars)
+	if e != nil {
+		br.Msg = "检测失败"
+		br.ErrMsg = "获取关联审批流失败, Err: " + e.Error()
+		return
+	}
+	if flowCount > 0 {
+		br.Msg = "该分类关联审批流,不允许删除"
+		br.Ret = 403
+		return
+	}
+
 	if err = models.DeleteEnglishClassify(classifyId); err != nil {
 		br.Msg = "删除失败"
 		br.ErrMsg = "删除报告失败, Err: " + err.Error()

+ 274 - 20
controllers/english_report/report.go

@@ -5,6 +5,7 @@ import (
 	"eta/eta_api/controllers"
 	"eta/eta_api/models"
 	"eta/eta_api/models/company"
+	"eta/eta_api/models/report_approve"
 	"eta/eta_api/models/system"
 	"eta/eta_api/services"
 	"eta/eta_api/services/alarm_msg"
@@ -85,6 +86,14 @@ func (this *EnglishReportController) Add() {
 		return
 	}
 
+	// 根据审批开关及审批流判断当前报告状态
+	state, e := services.CheckReportCurrState(report_approve.FlowReportTypeEnglish, req.ClassifyIdFirst, req.ClassifyIdSecond, models.ReportOperateAdd)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "校验报告当前状态失败, Err: " + e.Error()
+		return
+	}
+
 	item := new(models.EnglishReport)
 	item.AddType = req.AddType
 	item.ClassifyIdFirst = req.ClassifyIdFirst
@@ -95,7 +104,7 @@ func (this *EnglishReportController) Add() {
 	item.Abstract = req.Abstract
 	item.Author = req.Author
 	item.Frequency = req.Frequency
-	item.State = req.State
+	item.State = state
 	item.Content = html.EscapeString(req.Content)
 	item.Stage = maxStage + 1
 	item.ContentSub = html.EscapeString(contentSub)
@@ -189,15 +198,27 @@ func (this *EnglishReportController) Edit() {
 	}
 
 	var stage int
-	report, _ := models.GetEnglishReportById(int(req.ReportId))
-	if report != nil {
-		if report.ClassifyNameFirst != req.ClassifyNameFirst || report.ClassifyNameSecond != req.ClassifyNameSecond {
-			maxStage, _ := models.GetEnglishReportStageEdit(req.ClassifyIdFirst, req.ClassifyIdSecond, int(req.ReportId))
-			maxStage = maxStage + 1
-			stage = maxStage
-		} else {
-			stage = report.Stage
+	report, e := models.GetEnglishReportById(int(req.ReportId))
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "报告已被删除, 请刷新页面"
+			return
 		}
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取报告失败, Err: " + e.Error()
+		return
+	}
+	if report.State == models.ReportStatePublished || report.State == models.ReportStatePass {
+		br.Msg = "该报告已发布,不允许编辑"
+		br.ErrMsg = "该报告已发布,不允许编辑"
+		return
+	}
+	if report.ClassifyNameFirst != req.ClassifyNameFirst || report.ClassifyNameSecond != req.ClassifyNameSecond {
+		maxStage, _ := models.GetEnglishReportStageEdit(req.ClassifyIdFirst, req.ClassifyIdSecond, int(req.ReportId))
+		maxStage = maxStage + 1
+		stage = maxStage
+	} else {
+		stage = report.Stage
 	}
 
 	item := new(models.EnglishReport)
@@ -209,7 +230,7 @@ func (this *EnglishReportController) Edit() {
 	item.Abstract = req.Abstract
 	item.Author = req.Author
 	item.Frequency = req.Frequency
-	item.State = req.State
+	item.State = report.State // 编辑不变更状态
 	item.Stage = stage
 	item.Content = html.EscapeString(req.Content)
 	item.ContentSub = html.EscapeString(contentSub)
@@ -307,7 +328,7 @@ func (this *EnglishReportController) Detail() {
 // @Description 获取报告列表
 // @Param   PageSize   query   int  true       "每页数据条数"
 // @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
-// @Param   TimeType     query string true  "筛选的时间类别:publish_time(发布时间),modify_time(更新时间)"
+// @Param   TimeType     query string true  "筛选的时间类别:publish_time(发布时间),modify_time(更新时间);approve_time(审批时间)"
 // @Param   StartDate   query   string  true       "开始时间"
 // @Param   EndDate   query   string  true       "结束时间"
 // @Param   Frequency   query   string  true       "频度"
@@ -369,7 +390,7 @@ func (this *EnglishReportController) ListReport() {
 	if timeType == "" {
 		timeType = "publish_time"
 	}
-	if timeType != "publish_time" && timeType != "modify_time" {
+	if timeType != "publish_time" && timeType != "modify_time" && timeType != "approve_time" {
 		br.Msg = "请选择正确的时间"
 		br.ErrMsg = "请选择正确的时间"
 		return
@@ -491,7 +512,7 @@ func (this *EnglishReportController) ListReport() {
 		fieldArr := []string{
 			"id", "add_type", "classify_id_first", "classify_name_first", "classify_id_second", "classify_name_second", "title", "abstract", "author",
 			"frequency", "create_time", "modify_time", "state", "publish_time", "pre_publish_time", "stage", "msg_is_send", "report_code", "pv", "share_url",
-			"pv_email", "email_state", "from_report_id", "key_takeaways", "admin_id", "admin_real_name",
+			"pv_email", "email_state", "from_report_id", "key_takeaways", "admin_id", "admin_real_name", "approve_time",
 		}
 		items, e := models.GetEnglishReportList(condition, pars, companyType, startSize, pageSize, fieldArr)
 		if e != nil {
@@ -713,14 +734,33 @@ func (this *EnglishReportController) PublishReport() {
 		} else {
 			publishTime = time.Now().Format(utils.FormatDateTime)
 		}
-		if tmpErr = models.PublishEnglishReportById(report.Id, publishTime); tmpErr != nil {
-			br.Msg = "报告发布失败"
-			br.ErrMsg = "报告发布失败, Err:" + tmpErr.Error() + ", report_id:" + strconv.Itoa(report.Id)
+
+		// 根据审批开关及审批流判断当前报告状态
+		state, e := services.CheckReportCurrState(report_approve.FlowReportTypeEnglish, report.ClassifyIdFirst, report.ClassifyIdSecond, models.ReportOperatePublish)
+		if e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = "校验报告当前状态失败, Err: " + e.Error()
 			return
 		}
-		go func() {
-			_ = services.UpdateEnglishReportEs(report.Id, 2)
-		}()
+
+		if state == models.ReportStatePublished {
+			// 发布报告
+			if tmpErr = models.PublishEnglishReportById(report.Id, publishTime); tmpErr != nil {
+				br.Msg = "报告发布失败"
+				br.ErrMsg = "报告发布失败, Err:" + tmpErr.Error() + ", report_id:" + strconv.Itoa(report.Id)
+				return
+			}
+			go func() {
+				_ = services.UpdateEnglishReportEs(report.Id, 2)
+			}()
+		} else {
+			// 从无审批切换为有审批, 状态重置
+			if e = models.ResetEnglishReportById(report.Id, state); tmpErr != nil {
+				br.Msg = "操作失败"
+				br.ErrMsg = fmt.Sprintf("重置英文报告状态失败, Err: %s, ReportId: %d", e.Error(), report.Id)
+				return
+			}
+		}
 	}
 
 	br.Ret = 200
@@ -793,6 +833,18 @@ func (this *EnglishReportController) PrePublishReport() {
 		return
 	}
 
+	// 校验是否开启了审批流
+	opening, e := services.CheckReportOpenApprove(report_approve.FlowReportTypeEnglish, report.ClassifyIdFirst, report.ClassifyIdSecond)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "校验报告是否开启审批流失败, Err: " + e.Error()
+		return
+	}
+	if opening {
+		br.Msg = "报告已开启审批流, 不可设置定时发布"
+		return
+	}
+
 	var tmpErr error
 	if tmpErr = models.SetPrePublishEnglishReportById(report.Id, req.PrePublishTime); tmpErr != nil {
 		br.Msg = "设置定时发布失败"
@@ -828,7 +880,22 @@ func (this *EnglishReportController) PublishCancleReport() {
 		br.ErrMsg = "参数错误,报告id不可为空"
 		return
 	}
-	err = models.PublishCancelEnglishReport(req.ReportIds)
+	reportInfo, err := models.GetEnglishReportById(req.ReportIds)
+	if err != nil {
+		br.Msg = "获取报告信息失败"
+		br.ErrMsg = "获取报告信息失败,Err:" + err.Error()
+		return
+	}
+
+	// 根据审批开关及审批流判断当前报告状态
+	state, e := services.CheckReportCurrState(report_approve.FlowReportTypeEnglish, reportInfo.ClassifyIdFirst, reportInfo.ClassifyIdSecond, models.ReportOperateCancelPublish)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "校验报告当前状态失败, Err: " + e.Error()
+		return
+	}
+
+	err = models.PublishCancelEnglishReport(req.ReportIds, state)
 	if err != nil {
 		br.Msg = "取消发布失败"
 		br.ErrMsg = "取消发布失败,Err:" + err.Error()
@@ -1233,3 +1300,190 @@ func (this *EnglishReportController) EditPolicy() {
 	br.Msg = "保存成功"
 	br.Data = resp
 }
+
+// SubmitApprove
+// @Title 提交审批
+// @Description 提交审批接口
+// @Param	request	body models.ReportSubmitApproveReq true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /approve/submit [post]
+func (this *EnglishReportController) SubmitApprove() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		return
+	}
+	var req models.ReportSubmitApproveReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数有误"
+		br.ErrMsg = "参数解析失败, Err: " + e.Error()
+		return
+	}
+	reportId := req.ReportId
+	if reportId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("参数有误, ReportId: %d", req.ReportId)
+		return
+	}
+
+	reportOb := new(models.EnglishReport)
+	reportItem, e := reportOb.GetItemById(reportId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "报告已被删除, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取报告失败, Err: " + e.Error()
+		return
+	}
+
+	// 校验当前审批配置, 返回下一个状态
+	state, e := services.CheckReportCurrState(report_approve.FlowReportTypeEnglish, reportItem.ClassifyIdFirst, reportItem.ClassifyIdSecond, models.ReportOperateSubmitApprove)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "校验报告状态失败, Err: " + e.Error()
+		return
+	}
+
+	// 下一个状态不为待审批时, 仅更新状态
+	if state != models.ReportStateWaitApprove {
+		reportItem.State = state
+		e = reportItem.UpdateReport([]string{"State"})
+		if e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = "更新报告状态失败, Err: " + e.Error()
+			return
+		}
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "操作成功"
+		return
+	}
+
+	// 提交审批
+	approveId, e := services.SubmitReportApprove(report_approve.FlowReportTypeEnglish, reportItem.Id, reportItem.Title, reportItem.ClassifyIdFirst, reportItem.ClassifyIdSecond, sysUser.AdminId, sysUser.RealName)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "提交审批失败, Err: " + e.Error()
+		return
+	}
+	reportItem.ApproveId = approveId
+	reportItem.State = models.ReportStateWaitApprove
+	reportItem.ModifyTime = time.Now().Local()
+	e = reportItem.UpdateReport([]string{"ApproveId", "State", "ModifyTime"})
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "更新报告状态失败, Err: " + e.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// CancelApprove
+// @Title 撤销审批
+// @Description 撤销审批
+// @Param	request	body models.ReportCancelApproveReq true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /approve/cancel [post]
+func (this *EnglishReportController) CancelApprove() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		return
+	}
+	var req models.ReportCancelApproveReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数有误"
+		br.ErrMsg = "参数解析失败, Err: " + e.Error()
+		return
+	}
+	reportId := req.ReportId
+	if reportId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("参数有误, ReportId: %d", req.ReportId)
+		return
+	}
+
+	reportOb := new(models.EnglishReport)
+	reportItem, e := reportOb.GetItemById(reportId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "报告已被删除, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取报告失败, Err: " + e.Error()
+		return
+	}
+
+	// 校验当前审批配置, 返回下一个状态
+	state, e := services.CheckReportCurrState(report_approve.FlowReportTypeEnglish, reportItem.ClassifyIdFirst, reportItem.ClassifyIdSecond, models.ReportOperateCancelApprove)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "校验报告状态失败, Err: " + e.Error()
+		return
+	}
+
+	// 下一个状态不为待提交时, 仅更新状态
+	if state != models.ReportStateWaitSubmit {
+		reportItem.State = state
+		e = reportItem.UpdateReport([]string{"State"})
+		if e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = "更新报告状态失败, Err: " + e.Error()
+			return
+		}
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "操作成功"
+		return
+	}
+	//if reportItem.ApproveId <= 0 {
+	//	br.Msg = "报告审批不存在"
+	//	br.ErrMsg = fmt.Sprintf("报告审批不存在, ApproveId: %d", reportItem.ApproveId)
+	//	return
+	//}
+
+	// 撤销审批
+	e = services.CancelReportApprove(report_approve.FlowReportTypeEnglish, reportItem.Id, reportItem.ApproveId, sysUser.AdminId, sysUser.RealName)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "撤销审批失败, Err: " + e.Error()
+		return
+	}
+	//reportItem.ApproveId = 0
+	//reportItem.State = models.ReportStateWaitSubmit
+	//reportItem.ModifyTime = time.Now().Local()
+	//e = reportItem.UpdateReport([]string{"ApproveId", "State", "ModifyTime"})
+	//if e != nil {
+	//	br.Msg = "操作失败"
+	//	br.ErrMsg = "更新报告状态失败, Err: " + e.Error()
+	//	return
+	//}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}

+ 301 - 57
controllers/report.go

@@ -3,7 +3,7 @@ package controllers
 import (
 	"encoding/json"
 	"eta/eta_api/models"
-	"eta/eta_api/models/company"
+	"eta/eta_api/models/report_approve"
 	"eta/eta_api/services"
 	"eta/eta_api/services/alarm_msg"
 	"eta/eta_api/services/data"
@@ -41,7 +41,7 @@ type ReportUploadCommonController struct {
 // @Description 获取报告列表
 // @Param   PageSize   query   int  true       "每页数据条数"
 // @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
-// @Param   TimeType     query string true  "筛选的时间类别:publish_time(发布时间),modify_time(更新时间)"
+// @Param   TimeType     query string true  "筛选的时间类别:publish_time(发布时间),modify_time(更新时间);approve_time(审批时间)"
 // @Param   StartDate   query   string  true       "开始时间"
 // @Param   EndDate   query   string  true       "结束时间"
 // @Param   Frequency   query   string  true       "频度"
@@ -85,7 +85,7 @@ func (this *ReportController) ListReport() {
 	if timeType == "" {
 		timeType = "publish_time"
 	}
-	if timeType != "publish_time" && timeType != "modify_time" {
+	if timeType != "publish_time" && timeType != "modify_time" && timeType != "approve_time" {
 		br.Msg = "请选择正确的时间"
 		br.ErrMsg = "请选择正确的时间"
 		return
@@ -266,6 +266,8 @@ func (this *ReportController) PublishReport() {
 		br.ErrMsg = "参数错误,报告id不可为空"
 		return
 	}
+
+	// 这里实际上不会批量发布了...
 	reportArr := strings.Split(reportIds, ",")
 	tips := ""
 	for _, v := range reportArr {
@@ -314,28 +316,47 @@ func (this *ReportController) PublishReport() {
 				br.ErrMsg = "报告内容为空,不需要生成,report_id:" + strconv.Itoa(report.Id)
 				return
 			}
-			if tmpErr = models.PublishReportById(report.Id, publishTime); tmpErr != nil {
-				br.Msg = "报告发布失败"
-				br.ErrMsg = "报告发布失败, Err:" + tmpErr.Error() + ", report_id:" + strconv.Itoa(report.Id)
+
+			// 根据审批开关及审批流判断当前报告状态
+			state, e := services.CheckReportCurrState(report_approve.FlowReportTypeChinese, report.ClassifyIdFirst, report.ClassifyIdSecond, models.ReportOperatePublish)
+			if e != nil {
+				br.Msg = "操作失败"
+				br.ErrMsg = "校验报告当前状态失败, Err: " + e.Error()
 				return
 			}
 
-			go func() {
-				// 生成音频
-				if report.VideoUrl == "" {
-					_ = services.CreateVideo(report)
+			if state == models.ReportStatePublished {
+				// 发布报告
+				if tmpErr = models.PublishReportById(report.Id, publishTime); tmpErr != nil {
+					br.Msg = "报告发布失败"
+					br.ErrMsg = "报告发布失败, Err:" + tmpErr.Error() + ", report_id:" + strconv.Itoa(report.Id)
+					return
 				}
-				//// 推送找钢网
-				//if utils.RunMode == "release" && (report.ClassifyNameSecond == "知白守黑日评" || report.ClassifyNameSecond == "股债日评") {
-				//	_ = services.ZhaoGangSend(report)
-				//}
-				// 更新报告Es
-				_ = services.UpdateReportEs(report.Id, 2)
-			}()
+				go func() {
+					// 生成音频
+					if report.VideoUrl == "" {
+						_ = services.CreateVideo(report)
+					}
+					//// 推送找钢网
+					//if utils.RunMode == "release" && (report.ClassifyNameSecond == "知白守黑日评" || report.ClassifyNameSecond == "股债日评") {
+					//	_ = services.ZhaoGangSend(report)
+					//}
+					// 更新报告Es
+					_ = services.UpdateReportEs(report.Id, 2)
+				}()
+			} else {
+				// 从无审批切换为有审批, 状态重置
+				if e = models.ResetReportById(report.Id, state); tmpErr != nil {
+					br.Msg = "操作失败"
+					br.ErrMsg = fmt.Sprintf("重置报告状态失败, Err: %s, ReportId: %d", e.Error(), report.Id)
+					return
+				}
+			}
+
 			recordItem := &models.ReportStateRecord{
 				ReportId:   vint,
 				ReportType: 1,
-				State:      2,
+				State:      state,
 				AdminId:    this.SysUser.AdminId,
 				AdminName:  this.SysUser.AdminName,
 				CreateTime: time.Now(),
@@ -389,7 +410,16 @@ func (this *ReportController) PublishCancleReport() {
 	if reportInfo.MsgIsSend == 1 {
 		publishTimeNullFlag = false
 	}
-	err = models.PublishCancleReport(req.ReportIds, publishTimeNullFlag)
+
+	// 根据审批开关及审批流判断当前报告状态
+	state, e := services.CheckReportCurrState(report_approve.FlowReportTypeChinese, reportInfo.ClassifyIdFirst, reportInfo.ClassifyIdSecond, models.ReportOperateCancelPublish)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "校验报告当前状态失败, Err: " + e.Error()
+		return
+	}
+
+	err = models.PublishCancelReport(req.ReportIds, state, publishTimeNullFlag)
 	if err != nil {
 		br.Msg = "取消发布失败"
 		br.ErrMsg = "取消发布失败,Err:" + err.Error()
@@ -400,24 +430,24 @@ func (this *ReportController) PublishCancleReport() {
 		go services.UpdateReportEs(req.ReportIds, 1)
 	}
 
-	// 获取审批流设置
-	confKey := "approval_flow"
-	confTmp, e := company.GetConfigDetailByCode(confKey)
-	if e != nil {
-		br.Msg = "获取审批流配置失败"
-		br.ErrMsg = "获取审批流配置失败, Err: " + e.Error()
-		return
-	}
-	if confTmp.ConfigValue == "1" || confTmp.ConfigValue == "2" || confTmp.ConfigValue == "3" {
-		br.Msg = "撤销成功"
-	} else {
-		br.Msg = "取消发布成功"
-	}
+	//// 获取审批流设置
+	//confKey := "approval_flow"
+	//confTmp, e := company.GetConfigDetailByCode(confKey)
+	//if e != nil {
+	//	br.Msg = "获取审批流配置失败"
+	//	br.ErrMsg = "获取审批流配置失败, Err: " + e.Error()
+	//	return
+	//}
+	//if confTmp.ConfigValue == "1" || confTmp.ConfigValue == "2" || confTmp.ConfigValue == "3" {
+	//	br.Msg = "撤销成功"
+	//} else {
+	//	br.Msg = "取消发布成功"
+	//}
 
 	recordItem := &models.ReportStateRecord{
 		ReportId:   req.ReportIds,
 		ReportType: 1,
-		State:      1,
+		State:      state,
 		AdminId:    this.SysUser.AdminId,
 		AdminName:  this.SysUser.AdminName,
 		CreateTime: time.Now(),
@@ -514,6 +544,14 @@ func (this *ReportController) Add() {
 		return
 	}
 
+	// 根据审批开关及审批流判断当前报告状态
+	state, e := services.CheckReportCurrState(report_approve.FlowReportTypeChinese, req.ClassifyIdFirst, req.ClassifyIdSecond, models.ReportOperateAdd)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "校验报告当前状态失败, Err: " + e.Error()
+		return
+	}
+
 	item := new(models.Report)
 	item.AddType = req.AddType
 	item.ClassifyIdFirst = req.ClassifyIdFirst
@@ -524,7 +562,7 @@ func (this *ReportController) Add() {
 	item.Abstract = req.Abstract
 	item.Author = req.Author
 	item.Frequency = req.Frequency
-	item.State = req.State
+	item.State = state
 	item.Content = html.EscapeString(req.Content)
 	item.Stage = maxStage + 1
 	item.ContentSub = html.EscapeString(contentSub)
@@ -645,34 +683,41 @@ func (this *ReportController) Edit() {
 	}
 
 	var stage int
-	report, _ := models.GetReportById(int(req.ReportId))
-	if report != nil {
-		if report.ClassifyNameFirst != req.ClassifyNameFirst || report.ClassifyNameSecond != req.ClassifyNameSecond {
-			maxStage, _ := models.GetReportStageEdit(req.ClassifyIdFirst, req.ClassifyIdSecond, int(req.ReportId))
-			maxStage = maxStage + 1
-			stage = maxStage
-		} else {
-			stage = report.Stage
+	report, e := models.GetReportById(int(req.ReportId))
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "报告已被删除, 请刷新页面"
+			return
 		}
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取报告失败, Err: " + e.Error()
+		return
 	}
-	if report.State == 2 {
+	if report.State == models.ReportStatePublished || report.State == models.ReportStatePass {
 		br.Msg = "该报告已发布,不允许编辑"
 		br.ErrMsg = "该报告已发布,不允许编辑"
 		return
 	}
-	if req.State != report.State {
-		recordItem := &models.ReportStateRecord{
-			ReportId:   int(req.ReportId),
-			ReportType: 1,
-			State:      req.State,
-			AdminId:    this.SysUser.AdminId,
-			AdminName:  this.SysUser.AdminName,
-			CreateTime: time.Now(),
-		}
-		go func() {
-			_, _ = models.AddReportStateRecord(recordItem)
-		}()
-	}
+	if report.ClassifyNameFirst != req.ClassifyNameFirst || report.ClassifyNameSecond != req.ClassifyNameSecond {
+		maxStage, _ := models.GetReportStageEdit(req.ClassifyIdFirst, req.ClassifyIdSecond, int(req.ReportId))
+		maxStage = maxStage + 1
+		stage = maxStage
+	} else {
+		stage = report.Stage
+	}
+	//if req.State != report.State {
+	//	recordItem := &models.ReportStateRecord{
+	//		ReportId:   int(req.ReportId),
+	//		ReportType: 1,
+	//		State:      req.State,
+	//		AdminId:    this.SysUser.AdminId,
+	//		AdminName:  this.SysUser.AdminName,
+	//		CreateTime: time.Now(),
+	//	}
+	//	go func() {
+	//		_, _ = models.AddReportStateRecord(recordItem)
+	//	}()
+	//}
 
 	item := new(models.Report)
 	item.ClassifyIdFirst = req.ClassifyIdFirst
@@ -683,7 +728,7 @@ func (this *ReportController) Edit() {
 	item.Abstract = req.Abstract
 	item.Author = req.Author
 	item.Frequency = req.Frequency
-	item.State = req.State
+	item.State = report.State // 编辑不变更状态
 	item.Stage = stage
 	item.Content = html.EscapeString(req.Content)
 	item.ContentSub = html.EscapeString(contentSub)
@@ -3360,6 +3405,18 @@ func (this *ReportController) PrePublishReport() {
 		return
 	}
 
+	// 校验是否开启了审批流
+	opening, e := services.CheckReportOpenApprove(report_approve.FlowReportTypeChinese, report.ClassifyIdFirst, report.ClassifyIdSecond)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "校验报告是否开启审批流失败, Err: " + e.Error()
+		return
+	}
+	if opening {
+		br.Msg = "报告已开启审批流, 不可设置定时发布"
+		return
+	}
+
 	var tmpErr error
 	if tmpErr = models.SetPrePublishReportById(report.Id, req.PrePublishTime, req.PreMsgSend); tmpErr != nil {
 		br.Msg = "设置定时发布失败"
@@ -3371,3 +3428,190 @@ func (this *ReportController) PrePublishReport() {
 	br.Success = true
 	br.Msg = "定时发布成功"
 }
+
+// SubmitApprove
+// @Title 提交审批
+// @Description 提交审批接口
+// @Param	request	body models.ReportSubmitApproveReq true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /approve/submit [post]
+func (this *ReportController) SubmitApprove() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		return
+	}
+	var req models.ReportSubmitApproveReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数有误"
+		br.ErrMsg = "参数解析失败, Err: " + e.Error()
+		return
+	}
+	reportId := req.ReportId
+	if reportId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("参数有误, ReportId: %d", req.ReportId)
+		return
+	}
+
+	reportOb := new(models.Report)
+	reportItem, e := reportOb.GetItemById(reportId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "报告已被删除, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取报告失败, Err: " + e.Error()
+		return
+	}
+
+	// 校验当前审批配置, 返回下一个状态
+	state, e := services.CheckReportCurrState(report_approve.FlowReportTypeChinese, reportItem.ClassifyIdFirst, reportItem.ClassifyIdSecond, models.ReportOperateSubmitApprove)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "校验报告状态失败, Err: " + e.Error()
+		return
+	}
+
+	// 下一个状态不为待审批时, 仅更新状态
+	if state != models.ReportStateWaitApprove {
+		reportItem.State = state
+		e = reportItem.UpdateReport([]string{"State"})
+		if e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = "更新报告状态失败, Err: " + e.Error()
+			return
+		}
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "操作成功"
+		return
+	}
+
+	// 提交审批
+	approveId, e := services.SubmitReportApprove(report_approve.FlowReportTypeChinese, reportItem.Id, reportItem.Title, reportItem.ClassifyIdFirst, reportItem.ClassifyIdSecond, sysUser.AdminId, sysUser.RealName)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "提交审批失败, Err: " + e.Error()
+		return
+	}
+	reportItem.ApproveId = approveId
+	reportItem.State = models.ReportStateWaitApprove
+	reportItem.ModifyTime = time.Now().Local()
+	e = reportItem.UpdateReport([]string{"ApproveId", "State", "ModifyTime"})
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "更新报告状态失败, Err: " + e.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// CancelApprove
+// @Title 撤销审批
+// @Description 撤销审批
+// @Param	request	body models.ReportCancelApproveReq true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /approve/cancel [post]
+func (this *ReportController) CancelApprove() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		return
+	}
+	var req models.ReportCancelApproveReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数有误"
+		br.ErrMsg = "参数解析失败, Err: " + e.Error()
+		return
+	}
+	reportId := req.ReportId
+	if reportId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("参数有误, ReportId: %d", req.ReportId)
+		return
+	}
+
+	reportOb := new(models.Report)
+	reportItem, e := reportOb.GetItemById(reportId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "报告已被删除, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取报告失败, Err: " + e.Error()
+		return
+	}
+
+	// 校验当前审批配置, 返回下一个状态
+	state, e := services.CheckReportCurrState(report_approve.FlowReportTypeChinese, reportItem.ClassifyIdFirst, reportItem.ClassifyIdSecond, models.ReportOperateCancelApprove)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "校验报告状态失败, Err: " + e.Error()
+		return
+	}
+
+	// 下一个状态不为待提交时, 仅更新状态
+	if state != models.ReportStateWaitSubmit {
+		reportItem.State = state
+		e = reportItem.UpdateReport([]string{"State"})
+		if e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = "更新报告状态失败, Err: " + e.Error()
+			return
+		}
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "操作成功"
+		return
+	}
+	//if reportItem.ApproveId <= 0 {
+	//	br.Msg = "报告审批不存在"
+	//	br.ErrMsg = fmt.Sprintf("报告审批不存在, ApproveId: %d", reportItem.ApproveId)
+	//	return
+	//}
+
+	// 撤销审批
+	e = services.CancelReportApprove(report_approve.FlowReportTypeChinese, reportItem.Id, reportItem.ApproveId, sysUser.AdminId, sysUser.RealName)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "撤销审批失败, Err: " + e.Error()
+		return
+	}
+	//reportItem.ApproveId = 0
+	//reportItem.State = models.ReportStateWaitSubmit
+	//reportItem.ModifyTime = time.Now().Local()
+	//e = reportItem.UpdateReport([]string{"ApproveId", "State", "ModifyTime"})
+	//if e != nil {
+	//	br.Msg = "操作失败"
+	//	br.ErrMsg = "更新报告状态失败, Err: " + e.Error()
+	//	return
+	//}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}

+ 931 - 0
controllers/report_approve/report_approve.go

@@ -0,0 +1,931 @@
+package report_approve
+
+import (
+	"encoding/json"
+	"eta/eta_api/controllers"
+	"eta/eta_api/models"
+	"eta/eta_api/models/report_approve"
+	"eta/eta_api/services"
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"sort"
+	"strings"
+	"time"
+)
+
+// ReportApproveController 报告审批
+type ReportApproveController struct {
+	controllers.BaseAuthController
+}
+
+// List
+// @Title 审批列表
+// @Description 审批列表
+// @Param   PageSize			query	int		true	"每页数据条数"
+// @Param   CurrentIndex		query	int		true	"当前页页码"
+// @Param   ListType			query   int     true	"列表类型:1-待处理;2-已处理;3-我发起的"
+// @Param   ReportType			query   int     false	"报告类型:1-中文研报;2-英文研报;3-智能研报"
+// @Param   ClassifyIdFirst		query	int		false	"一级分类ID"
+// @Param   ClassifyIdSecond	query	int		false	"二级分类ID"
+// @Param   Keyword				query	string	false	"搜索关键词"
+// @Param   ApproveState		query	int		false	"审批状态:1-待审批;2-已审批;3-已驳回;4-已撤回"
+// @Param   TimeType			query	int		false	"时间类型:1-提交时间;2-处理时间;3-审批时间"
+// @Param   StartTime			query	string	false	"开始时间"
+// @Param   EndTime				query	string	false	"结束时间"
+// @Param   SortField			query	int		false	"排序字段:1-提交时间;2-处理时间;3-审批时间"
+// @Param   SortRule			query	int		false	"排序方式: 1-正序; 2-倒序(默认)"
+// @Success 200 {object} report_approve.ReportApproveListResp
+// @router /list [get]
+func (this *ReportApproveController) List() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	params := new(report_approve.ReportApproveListReq)
+	if e := this.ParseForm(params); e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "入参解析失败, Err: " + e.Error()
+		return
+	}
+	if params.ListType != 1 && params.ListType != 2 && params.ListType != 3 {
+		br.Msg = "列表类型有误"
+		return
+	}
+
+	// 报告分类
+	cnClassifyIdName, enClassifyIdName := make(map[int]string), make(map[int]string)
+	cnClassify, e := models.GetAllClassify()
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取中文分类失败, Err: " + e.Error()
+		return
+	}
+	for _, v := range cnClassify {
+		cnClassifyIdName[v.Id] = v.ClassifyName
+	}
+	enClassify, e := models.GetEnglishClassifies()
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取英文分类失败, Err: " + e.Error()
+		return
+	}
+	enRootIdMap := make(map[int]int) // 英文分类的一级分类ID
+	for _, v := range enClassify {
+		enClassifyIdName[v.Id] = v.ClassifyName
+		enRootIdMap[v.Id] = v.RootId
+	}
+
+	// 分页
+	var startSize int
+	if params.PageSize <= 0 {
+		params.PageSize = utils.PageSize20
+	}
+	if params.CurrentIndex <= 0 {
+		params.CurrentIndex = 1
+	}
+	startSize = utils.StartIndex(params.CurrentIndex, params.PageSize)
+
+	resp := new(report_approve.ReportApproveListResp)
+	respTotal := 0
+	respList := make([]*report_approve.ReportApproveItem, 0)
+	timeField := map[int]string{1: fmt.Sprintf("a.%s", report_approve.ReportApproveCols.CreateTime), 2: fmt.Sprintf("a.%s", report_approve.ReportApproveRecordCols.ApproveTime), 3: fmt.Sprintf("b.%s", report_approve.ReportApproveCols.ApproveTime)}
+	mineTimeField := map[int]string{1: fmt.Sprintf("%s", report_approve.ReportApproveCols.CreateTime), 3: fmt.Sprintf("%s", report_approve.ReportApproveCols.ApproveTime)}
+	orderRules := map[int]string{1: "ASC", 2: "DESC"}
+	ormList := make([]*report_approve.ReportApproveItemOrm, 0)
+
+	// 待处理
+	if params.ListType == 1 {
+		cond := fmt.Sprintf(` AND a.%s = ? AND b.%s = ? AND a.%s = ?`, report_approve.ReportApproveRecordCols.State, report_approve.ReportApproveCols.State, report_approve.ReportApproveRecordCols.ApproveUserId)
+		pars := make([]interface{}, 0)
+		pars = append(pars, report_approve.ReportApproveStateApproving, report_approve.ReportApproveStateApproving, sysUser.AdminId)
+		order := ""
+
+		// 筛选条件
+		if params.ReportType > 0 && params.ClassifySecondId > 0 {
+			cond += fmt.Sprintf(` AND b.%s = ? AND b.%s = ?`, report_approve.ReportApproveCols.ReportType, report_approve.ReportApproveCols.ClassifySecondId)
+			pars = append(pars, params.ReportType, params.ClassifySecondId)
+		}
+		if params.TimeType <= 0 {
+			params.TimeType = 1
+		}
+		if params.TimeType == 1 && params.StartTime != "" && params.EndTime != "" {
+			_, e := time.Parse(utils.FormatDate, params.StartTime)
+			if e != nil {
+				br.Msg = "开始时间格式有误"
+				return
+			}
+			_, e = time.Parse(utils.FormatDate, params.EndTime)
+			if e != nil {
+				br.Msg = "结束时间格式有误"
+				return
+			}
+			st := fmt.Sprintf("%s %s", params.StartTime, "00:00:00")
+			ed := fmt.Sprintf("%s %s", params.EndTime, "23:59:59")
+			cond += fmt.Sprintf(` AND (b.%s BETWEEN ? AND ?)`, report_approve.ReportApproveCols.CreateTime)
+			pars = append(pars, st, ed)
+		}
+		params.Keyword = strings.TrimSpace(params.Keyword)
+		if params.Keyword != "" {
+			kw := fmt.Sprint("%", params.Keyword, "%")
+			cond += fmt.Sprintf(` AND b.%s LIKE ?`, report_approve.ReportApproveCols.ReportTitle)
+			pars = append(pars, kw)
+		}
+		if params.SortField > 0 && params.SortRule > 0 {
+			orderField := timeField[params.SortField]
+			if orderField == "" {
+				br.Msg = "时间排序字段有误"
+				return
+			}
+			orderRule := orderRules[params.SortRule]
+			if orderRule == "" {
+				br.Msg = "时间排序方式有误"
+				return
+			}
+			order = fmt.Sprintf("%s %s", orderField, orderRule)
+		}
+		total, e := report_approve.GetApprovingReportApproveCount(cond, pars)
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "GetApprovingReportApproveCount err: " + e.Error()
+			return
+		}
+		respTotal = total
+		list, e := report_approve.GetApprovingReportApprovePageList(cond, pars, order, startSize, params.PageSize)
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "GetApprovingReportApprovePageList err: " + e.Error()
+			return
+		}
+		ormList = list
+	}
+
+	// 已处理
+	if params.ListType == 2 {
+		cond := fmt.Sprintf(` AND a.%s = ? AND a.%s IN (%s)`, report_approve.ReportApproveRecordCols.ApproveUserId, report_approve.ReportApproveRecordCols.State, utils.GetOrmInReplace(2))
+		pars := make([]interface{}, 0)
+		pars = append(pars, sysUser.AdminId, []int{report_approve.ReportApproveStatePass, report_approve.ReportApproveStateRefuse})
+		order := ""
+
+		// 筛选条件
+		if params.ReportType > 0 && params.ClassifySecondId > 0 {
+			cond += fmt.Sprintf(` AND b.%s = ? AND b.%s = ?`, report_approve.ReportApproveCols.ReportType, report_approve.ReportApproveCols.ClassifySecondId)
+			pars = append(pars, params.ReportType, params.ClassifySecondId)
+		}
+		if params.TimeType > 0 && params.StartTime != "" && params.EndTime != "" {
+			_, e := time.Parse(utils.FormatDate, params.StartTime)
+			if e != nil {
+				br.Msg = "开始时间格式有误"
+				return
+			}
+			_, e = time.Parse(utils.FormatDate, params.EndTime)
+			if e != nil {
+				br.Msg = "结束时间格式有误"
+				return
+			}
+			st := fmt.Sprintf("%s %s", params.StartTime, "00:00:00")
+			ed := fmt.Sprintf("%s %s", params.EndTime, "23:59:59")
+			cond += fmt.Sprintf(` AND (%s BETWEEN ? AND ?)`, timeField[params.TimeType])
+			pars = append(pars, st, ed)
+		}
+		params.Keyword = strings.TrimSpace(params.Keyword)
+		if params.Keyword != "" {
+			kw := fmt.Sprint("%", params.Keyword, "%")
+			cond += fmt.Sprintf(` AND b.%s LIKE ?`, report_approve.ReportApproveCols.ReportTitle)
+			pars = append(pars, kw)
+		}
+		if params.SortField > 0 && params.SortRule > 0 {
+			orderField := timeField[params.SortField]
+			if orderField == "" {
+				br.Msg = "时间排序字段有误"
+				return
+			}
+			orderRule := orderRules[params.SortRule]
+			if orderRule == "" {
+				br.Msg = "时间排序方式有误"
+				return
+			}
+			order = fmt.Sprintf("%s %s", orderField, orderRule)
+		}
+		if params.ApproveState > 0 {
+			cond += fmt.Sprintf(` AND a.%s = ?`, report_approve.ReportApproveRecordCols.State)
+			pars = append(pars, params.ApproveState)
+		}
+		total, e := report_approve.GetApprovedReportApproveCount(cond, pars)
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "GetApprovedReportApproveCount err: " + e.Error()
+			return
+		}
+		respTotal = total
+		list, e := report_approve.GetApprovedReportApprovePageList(cond, pars, order, startSize, params.PageSize)
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "GetApprovedReportApprovePageList err: " + e.Error()
+			return
+		}
+		ormList = list
+	}
+
+	// 我发起的
+	if params.ListType == 3 {
+		cond := fmt.Sprintf(` AND a.%s = ?`, report_approve.ReportApproveCols.ApplyUserId)
+		pars := make([]interface{}, 0)
+		pars = append(pars, sysUser.AdminId)
+		order := ""
+
+		// 筛选条件
+		if params.ReportType > 0 && params.ClassifySecondId > 0 {
+			cond += fmt.Sprintf(` AND a.%s = ? AND a.%s = ?`, report_approve.ReportApproveCols.ReportType, report_approve.ReportApproveCols.ClassifySecondId)
+			pars = append(pars, params.ReportType, params.ClassifySecondId)
+		}
+		if params.TimeType > 0 && params.StartTime != "" && params.EndTime != "" {
+			_, e := time.Parse(utils.FormatDate, params.StartTime)
+			if e != nil {
+				br.Msg = "开始时间格式有误"
+				return
+			}
+			_, e = time.Parse(utils.FormatDate, params.EndTime)
+			if e != nil {
+				br.Msg = "结束时间格式有误"
+				return
+			}
+			st := fmt.Sprintf("%s %s", params.StartTime, "00:00:00")
+			ed := fmt.Sprintf("%s %s", params.EndTime, "23:59:59")
+			field := mineTimeField[params.TimeType]
+			if field == "" {
+				br.Msg = "筛选时间有误"
+				return
+			}
+			cond += fmt.Sprintf(` AND (%s BETWEEN ? AND ?)`, field)
+			pars = append(pars, st, ed)
+		}
+		params.Keyword = strings.TrimSpace(params.Keyword)
+		if params.Keyword != "" {
+			kw := fmt.Sprint("%", params.Keyword, "%")
+			cond += fmt.Sprintf(` AND a.%s LIKE ?`, report_approve.ReportApproveCols.ReportTitle)
+			pars = append(pars, kw)
+		}
+		if params.SortField > 0 && params.SortRule > 0 {
+			orderField := mineTimeField[params.SortField]
+			if orderField == "" {
+				br.Msg = "时间排序字段有误"
+				return
+			}
+			orderRule := orderRules[params.SortRule]
+			if orderRule == "" {
+				br.Msg = "时间排序方式有误"
+				return
+			}
+			order = fmt.Sprintf("%s %s", orderField, orderRule)
+		}
+		if params.ApproveState > 0 {
+			cond += fmt.Sprintf(` AND a.%s = ?`, report_approve.ReportApproveRecordCols.State)
+			pars = append(pars, params.ApproveState)
+		}
+		total, e := report_approve.GetApplyReportApproveCount(cond, pars)
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "GetApplyReportApproveCount err: " + e.Error()
+			return
+		}
+		respTotal = total
+		list, e := report_approve.GetApplyReportApprovePageList(cond, pars, order, startSize, params.PageSize)
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "GetApplyReportApprovePageList err: " + e.Error()
+			return
+		}
+		ormList = list
+	}
+
+	// 格式化列表
+	for _, v := range ormList {
+		t := report_approve.FormatReportApproveOrm2Item(v)
+		if v.ReportType == report_approve.FlowReportTypeEnglish {
+			t.ReportClassify = fmt.Sprintf("%s/%s/%s/%s", report_approve.FlowReportTypeMap[v.ReportType], enClassifyIdName[enRootIdMap[v.ClassifySecondId]], enClassifyIdName[v.ClassifyFirstId], enClassifyIdName[v.ClassifySecondId])
+		} else {
+			t.ReportClassify = fmt.Sprintf("%s/%s/%s", report_approve.FlowReportTypeMap[v.ReportType], cnClassifyIdName[v.ClassifyFirstId], cnClassifyIdName[v.ClassifySecondId])
+		}
+		respList = append(respList, t)
+	}
+
+	page := paging.GetPaging(params.CurrentIndex, params.PageSize, respTotal)
+	resp.Paging = page
+	resp.List = respList
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// Detail
+// @Title 审批详情
+// @Description 审批详情
+// @Param   ReportApproveId  query  int  true  "审批ID"
+// @Success 200 {object} report_approve.ReportApproveDetail
+// @router /detail [get]
+func (this *ReportApproveController) Detail() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	approveId, _ := this.GetInt("ReportApproveId")
+	if approveId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("参数有误, ReportApproveId: %d", approveId)
+		return
+	}
+
+	approveOb := new(report_approve.ReportApprove)
+	approveItem, e := approveOb.GetItemById(approveId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "审批已被删除, 请刷新页面"
+			return
+		}
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取审批失败, Err: " + e.Error()
+		return
+	}
+
+	// 审批信息
+	detail := new(report_approve.ReportApproveDetail)
+	detail.Approve = new(report_approve.ReportApproveDetailItem)
+	detail.Approve.ReportApproveId = approveItem.ReportApproveId
+	detail.Approve.State = approveItem.State
+	detail.Approve.FlowId = approveItem.FlowId
+	detail.Approve.FlowVersion = approveItem.FlowVersion
+	detail.Approve.StartNodeId = approveItem.StartNodeId
+	detail.Approve.CurrNodeId = approveItem.CurrNodeId
+	detail.Approve.ApplyUserId = approveItem.ApplyUserId
+	detail.Approve.ApplyUserName = approveItem.ApplyUserName
+	detail.Approve.ApproveTime = utils.TimeTransferString(utils.FormatDateTime, approveItem.ApproveTime)
+	detail.Approve.CreateTime = utils.TimeTransferString(utils.FormatDateTime, approveItem.CreateTime)
+	detail.Approve.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, approveItem.ModifyTime)
+
+	// 审批流
+	//flowOb := new(report_approve.ReportApproveFlow)
+	//flowItem, e := flowOb.GetItemById(approveItem.FlowId)
+	//if e != nil {
+	//	if e.Error() == utils.ErrNoRow() {
+	//		br.Msg = "审批流已被删除, 请刷新页面"
+	//		return
+	//	}
+	//	br.Msg = "获取失败"
+	//	br.ErrMsg = "获取审批流失败, Err: " + e.Error()
+	//	return
+	//}
+	//detail.ApproveFlow = report_approve.FormatReportApproveFlow2Item(flowItem)
+
+	// 审批节点
+	nodeOb := new(report_approve.ReportApproveNode)
+	nodeCond := fmt.Sprintf(` AND %s = ? AND %s = ?`, report_approve.ReportApproveNodeCols.ReportApproveFlowId, report_approve.ReportApproveNodeCols.CurrVersion)
+	nodePars := make([]interface{}, 0)
+	nodePars = append(nodePars, approveItem.FlowId, approveItem.FlowVersion)
+	nodeItems, e := nodeOb.GetItemsByCondition(nodeCond, nodePars, []string{}, "")
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取审批节点失败, Err: " + e.Error()
+		return
+	}
+
+	// 审批记录
+	recordOb := new(report_approve.ReportApproveRecord)
+	recordCond := fmt.Sprintf(` AND %s = ?`, report_approve.ReportApproveRecordCols.ReportApproveId)
+	recordPars := make([]interface{}, 0)
+	recordPars = append(recordPars, approveItem.ReportApproveId)
+	recordItems, e := recordOb.GetItemsByCondition(recordCond, recordPars, []string{}, fmt.Sprintf("%s DESC", report_approve.ReportApproveRecordCols.ApproveTime))
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取审批记录失败, Err: " + e.Error()
+		return
+	}
+	recordMap := make(map[string]*report_approve.ReportApproveRecord)
+	for _, v := range recordItems {
+		k := fmt.Sprintf("%d-%d", v.NodeId, v.ApproveUserId)
+		recordMap[k] = v
+	}
+
+	// 审批流节点详情
+	detail.ApproveFlowNodes = make([]*report_approve.ReportApproveDetailNodes, 0)
+	for _, v := range nodeItems {
+		t := new(report_approve.ReportApproveDetailNodes)
+		t.ReportApproveNodeId = v.ReportApproveNodeId
+		t.ReportApproveFlowId = v.ReportApproveFlowId
+		t.PrevNodeId = v.PrevNodeId
+		t.NextNodeId = v.NextNodeId
+		t.NodeType = v.NodeType
+		t.ApproveType = v.ApproveType
+		t.Users = make([]*report_approve.ReportApproveDetailNodeUser, 0)
+		us := make([]*report_approve.ReportApproveNodeUserReq, 0)
+		if v.Users != "" {
+			e = json.Unmarshal([]byte(v.Users), &us)
+			if e != nil {
+				br.Msg = "获取失败"
+				br.ErrMsg = "节点用户解析失败, Err: " + e.Error()
+				return
+			}
+		}
+		for _, vu := range us {
+			u := new(report_approve.ReportApproveDetailNodeUser)
+			u.UserType = vu.UserType
+			u.UserId = vu.UserId
+			u.UserName = vu.UserName
+			u.Sort = vu.Sort
+			// 审批记录
+			k := fmt.Sprintf("%d-%d", v.ReportApproveNodeId, vu.UserId)
+			r := recordMap[k]
+			if r != nil {
+				u.ApproveRecord = new(report_approve.ReportApproveDetailNodeUserRecord)
+				u.ApproveRecord.ReportApproveRecordId = r.ReportApproveRecordId
+				u.ApproveRecord.State = r.State
+				u.ApproveRecord.ApproveUserId = r.ApproveUserId
+				u.ApproveRecord.ApproveUserName = r.ApproveUserName
+				u.ApproveRecord.ApproveRemark = r.ApproveRemark
+				u.ApproveRecord.ApproveTime = utils.TimeTransferString(utils.FormatDateTime, r.ApproveTime)
+			}
+			t.Users = append(t.Users, u)
+		}
+		sort.Slice(t.Users, func(k, j int) bool {
+			return t.Users[k].Sort < t.Users[j].Sort
+		})
+		detail.ApproveFlowNodes = append(detail.ApproveFlowNodes, t)
+	}
+
+	// 报告信息
+	cnClassifyIdName, enClassifyIdName := make(map[int]string), make(map[int]string)
+	cnClassify, e := models.GetAllClassify()
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取中文分类失败, Err: " + e.Error()
+		return
+	}
+	for _, v := range cnClassify {
+		cnClassifyIdName[v.Id] = v.ClassifyName
+	}
+	enClassify, e := models.GetEnglishClassifies()
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取英文分类失败, Err: " + e.Error()
+		return
+	}
+	enRootIdMap := make(map[int]int) // 英文分类的一级分类ID
+	for _, v := range enClassify {
+		enClassifyIdName[v.Id] = v.ClassifyName
+		enRootIdMap[v.Id] = v.RootId
+	}
+	detail.Report = new(report_approve.ReportApproveDetailReport)
+	detail.Report.ReportType = approveItem.ReportType
+	detail.Report.ReportId = approveItem.ReportId
+	detail.Report.ReportTitle = approveItem.ReportTitle
+	// 报告分类路由
+	if approveItem.ReportType == report_approve.FlowReportTypeChinese {
+		detail.Report.ReportClassify = fmt.Sprintf("%s/%s/%s", report_approve.FlowReportTypeMap[approveItem.ReportType], cnClassifyIdName[approveItem.ClassifyFirstId], cnClassifyIdName[approveItem.ClassifySecondId])
+	}
+	if approveItem.ReportType == report_approve.FlowReportTypeEnglish {
+		detail.Report.ReportClassify = fmt.Sprintf("%s/%s/%s/%s", report_approve.FlowReportTypeMap[approveItem.ReportType], enClassifyIdName[enRootIdMap[approveItem.ClassifySecondId]], enClassifyIdName[approveItem.ClassifyFirstId], enClassifyIdName[approveItem.ClassifySecondId])
+	}
+	if approveItem.ReportType == report_approve.FlowReportTypeSmart {
+		detail.Report.ReportClassify = fmt.Sprintf("%s/%s/%s", report_approve.FlowReportTypeMap[approveItem.ReportType], cnClassifyIdName[approveItem.ClassifyFirstId], cnClassifyIdName[approveItem.ClassifySecondId])
+	}
+
+	br.Data = detail
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// Approve
+// @Title 通过审批
+// @Description 通过审批
+// @Param	request	body report_approve.ReportApprovePassReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /approve [post]
+func (this *ReportApproveController) Approve() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req report_approve.ReportApprovePassReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数有误"
+		br.ErrMsg = "参数解析失败, Err: " + e.Error()
+		return
+	}
+	if req.ReportApproveId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("参数有误, ReportApproveId: %d", req.ReportApproveId)
+		return
+	}
+
+	approveOb := new(report_approve.ReportApprove)
+	approveItem, e := approveOb.GetItemById(req.ReportApproveId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "审批不存在, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取审批信息失败, Err: " + e.Error()
+		return
+	}
+	if approveItem.State != report_approve.ReportApproveStateApproving {
+		br.Msg = "审批状态有误, 请刷新页面"
+		br.ErrMsg = fmt.Sprintf("审批状态有误, State: %d", approveItem.State)
+		return
+	}
+
+	// 校验审批记录和审批
+	recordOb := new(report_approve.ReportApproveRecord)
+	recordCond := fmt.Sprintf(` AND %s = ? AND %s = ? AND %s = ?`, report_approve.ReportApproveRecordCols.ReportApproveId, report_approve.ReportApproveRecordCols.ApproveUserId, report_approve.ReportApproveRecordCols.State)
+	recordPars := make([]interface{}, 0)
+	recordPars = append(recordPars, approveItem.ReportApproveId, sysUser.AdminId, report_approve.ReportApproveStateApproving)
+	recordItem, e := recordOb.GetItemByCondition(recordCond, recordPars, "")
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "无权审批"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取审批记录失败, Err: " + e.Error()
+		return
+	}
+
+	// 通过审批
+	tips, e := services.PassReportApprove(approveItem, recordItem, sysUser.AdminId)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "通过审批失败, Err: " + e.Error()
+		return
+	}
+	if tips != "" {
+		br.Msg = tips
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// Refuse
+// @Title 驳回审批
+// @Description 驳回审批
+// @Param	request	body report_approve.ReportApproveRefuseReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /refuse [post]
+func (this *ReportApproveController) Refuse() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req report_approve.ReportApproveRefuseReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数有误"
+		br.ErrMsg = "参数解析失败, Err: " + e.Error()
+		return
+	}
+	if req.ReportApproveId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("参数有误, ReportApproveId: %d", req.ReportApproveId)
+		return
+	}
+
+	approveOb := new(report_approve.ReportApprove)
+	approveItem, e := approveOb.GetItemById(req.ReportApproveId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "审批不存在, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取审批信息失败, Err: " + e.Error()
+		return
+	}
+	if approveItem.State != report_approve.ReportApproveStateApproving {
+		br.Msg = "审批状态有误, 请刷新页面"
+		br.ErrMsg = fmt.Sprintf("审批状态有误, State: %d", approveItem.State)
+		return
+	}
+
+	// 校验审批记录和审批
+	recordOb := new(report_approve.ReportApproveRecord)
+	recordCond := fmt.Sprintf(` AND %s = ? AND %s = ? AND %s = ?`, report_approve.ReportApproveRecordCols.ReportApproveId, report_approve.ReportApproveRecordCols.ApproveUserId, report_approve.ReportApproveRecordCols.State)
+	recordPars := make([]interface{}, 0)
+	recordPars = append(recordPars, approveItem.ReportApproveId, sysUser.AdminId, report_approve.ReportApproveStateApproving)
+	recordItem, e := recordOb.GetItemByCondition(recordCond, recordPars, "")
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "无权审批"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取审批记录失败, Err: " + e.Error()
+		return
+	}
+
+	// 驳回审批
+	if e = services.RefuseReportApprove(approveItem, recordItem, req.ApproveRemark, sysUser.AdminId); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "驳回审批失败, Err: " + e.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// Cancel
+// @Title 撤销审批
+// @Description 撤销审批
+// @Param	request	body report_approve.ReportApproveCancelReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /cancel [post]
+func (this *ReportApproveController) Cancel() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req report_approve.ReportApproveCancelReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数有误"
+		br.ErrMsg = "参数解析失败, Err: " + e.Error()
+		return
+	}
+	if req.ReportApproveId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("参数有误, ReportApproveId: %d", req.ReportApproveId)
+		return
+	}
+
+	approveOb := new(report_approve.ReportApprove)
+	approveItem, e := approveOb.GetItemById(req.ReportApproveId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "审批已被删除, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取审批失败, Err: " + e.Error()
+		return
+	}
+	if approveItem.ApplyUserId != sysUser.AdminId {
+		br.Msg = "非申请人不可撤销"
+		return
+	}
+
+	// 撤销审批
+	e = services.CancelReportApprove(approveItem.ReportType, approveItem.ReportId, approveItem.ReportApproveId, sysUser.AdminId, sysUser.RealName)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "撤销审批失败, Err: " + e.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// MessageList
+// @Title 审批消息列表
+// @Description 审批消息列表
+// @Param   PageSize			query	int		true	"每页数据条数"
+// @Param   CurrentIndex		query	int		true	"当前页页码"
+// @Success 200 {object} report_approve.ReportApproveMessageListResp
+// @router /message/list [get]
+func (this *ReportApproveController) MessageList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	params := new(report_approve.ReportApproveListReq)
+	if e := this.ParseForm(params); e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "入参解析失败, Err: " + e.Error()
+		return
+	}
+
+	// 分页
+	var startSize int
+	if params.PageSize <= 0 {
+		params.PageSize = utils.PageSize20
+	}
+	if params.CurrentIndex <= 0 {
+		params.CurrentIndex = 1
+	}
+	startSize = utils.StartIndex(params.CurrentIndex, params.PageSize)
+
+	resp := new(report_approve.ReportApproveMessageListResp)
+	resp.List = make([]*report_approve.ReportApproveMessageItem, 0)
+	cond := fmt.Sprintf(` AND %s = ?`, report_approve.ReportApproveMessageCols.ReceiveUserId)
+	pars := make([]interface{}, 0)
+	pars = append(pars, sysUser.AdminId)
+	order := fmt.Sprintf(`%s ASC, %s DESC`, report_approve.ReportApproveMessageCols.IsRead, report_approve.ReportApproveMessageCols.CreateTime)
+
+	messageOb := new(report_approve.ReportApproveMessage)
+	total, e := messageOb.GetCountByCondition(cond, pars)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取审批消息列表总数失败, Err: " + e.Error()
+		return
+	}
+	list, e := messageOb.GetPageItemsByCondition(cond, pars, []string{}, order, startSize, params.PageSize)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取审批消息列表失败, Err: " + e.Error()
+		return
+	}
+	for _, v := range list {
+		t := report_approve.FormatReportApproveMessage2Item(v)
+		resp.List = append(resp.List, t)
+	}
+
+	// 未读消息数
+	cond += fmt.Sprintf(` AND %s = ?`, report_approve.ReportApproveMessageCols.IsRead)
+	pars = append(pars, 0)
+	unreadTotal, e := messageOb.GetCountByCondition(cond, pars)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取审批消息列表总数失败, Err: " + e.Error()
+		return
+	}
+	resp.UnreadTotal = unreadTotal
+
+	page := paging.GetPaging(params.CurrentIndex, params.PageSize, total)
+	resp.Paging = page
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// MessageRead
+// @Title 消息已读
+// @Description 消息已读
+// @Param	request	body report_approve.ReportApproveMessageReadReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /message/read [post]
+func (this *ReportApproveController) MessageRead() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req report_approve.ReportApproveMessageReadReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数有误"
+		br.ErrMsg = "参数解析失败, Err: " + e.Error()
+		return
+	}
+	if req.MessageId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("参数有误, MessageId: %d", req.MessageId)
+		return
+	}
+
+	messageOb := new(report_approve.ReportApproveMessage)
+	messageItem, e := messageOb.GetItemById(req.MessageId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "消息不存在, 请刷新页面"
+			return
+		}
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取审批消息失败, Err: " + e.Error()
+		return
+	}
+	messageItem.IsRead = 1
+	messageItem.ModifyTime = time.Now().Local()
+	cols := []string{"IsRead", "ModifyTime"}
+	if e = messageItem.Update(cols); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "更新审批消息已读失败, Err: " + e.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// CheckApproveOpen
+// @Title 校验分类是否开启审批
+// @Description 校验分类是否开启审批
+// @Param	request	body report_approve.ReportApproveCheckApproveOpenReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /classify/check_open [post]
+func (this *ReportApproveController) CheckApproveOpen() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req report_approve.ReportApproveCheckApproveOpenReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数有误"
+		br.ErrMsg = "参数解析失败, Err: " + e.Error()
+		return
+	}
+
+	// 校验是否开启了审批流
+	opening, e := services.CheckReportOpenApprove(req.ReportType, req.ClassifyFirstId, req.ClassifySecondId)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "校验报告是否开启审批流失败, Err: " + e.Error()
+		return
+	}
+
+	br.Data = opening
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}

+ 741 - 0
controllers/report_approve/report_approve_flow.go

@@ -0,0 +1,741 @@
+package report_approve
+
+import (
+	"encoding/json"
+	"eta/eta_api/controllers"
+	"eta/eta_api/models"
+	"eta/eta_api/models/report_approve"
+	"eta/eta_api/services"
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"strings"
+	"sync"
+	"time"
+)
+
+// ReportApproveFlowController 报告审批流
+type ReportApproveFlowController struct {
+	controllers.BaseAuthController
+}
+
+// List
+// @Title 报告列表
+// @Description 报告列表
+// @Param   PageSize			query	int		true	"每页数据条数"
+// @Param   CurrentIndex		query	int		true	"当前页页码"
+// @Param   ReportType			query   int     false	"报告类型:1-中文研报;2-英文研报;3-智能研报"
+// @Param   ClassifyIdFirst		query	int		false	"一级分类ID"
+// @Param   ClassifyIdSecond	query	int		false	"二级分类ID"
+// @Param   Keyword				query	string	false	"搜索关键词"
+// @Param   SortRule			query	int		false	"排序方式: 1-正序; 2-倒序(默认)"
+// @Success 200 {object} report_approve.ReportApproveFlowListResp
+// @router /flow/list [get]
+func (this *ReportApproveFlowController) List() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	params := new(report_approve.ReportApproveFlowListReq)
+	if e := this.ParseForm(params); e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "入参解析失败, Err: " + e.Error()
+		return
+	}
+
+	var cond, orderRule string
+	var pars []interface{}
+	// 筛选项
+	{
+		keyword := strings.TrimSpace(params.Keyword)
+		if keyword != "" {
+			kw := fmt.Sprint("%", keyword, "%")
+			cond += fmt.Sprintf(` AND %s LIKE ?`, report_approve.ReportApproveFlowCols.FlowName)
+			pars = append(pars, kw)
+		}
+		if params.ReportType > 0 && params.ClassifySecondId > 0 {
+			cond += fmt.Sprintf(` AND %s = ? AND %s = ?`, report_approve.ReportApproveFlowCols.ReportType, report_approve.ReportApproveFlowCols.ClassifySecondId)
+			pars = append(pars, params.ReportType, params.ClassifySecondId)
+		}
+		if params.SortRule > 0 {
+			orderMap := map[int]string{1: "ASC", 2: "DESC"}
+			orderRule = fmt.Sprintf("%s %s", report_approve.ReportApproveFlowCols.CreateTime, orderMap[params.SortRule])
+		}
+	}
+
+	resp := new(report_approve.ReportApproveFlowListResp)
+	flowOb := new(report_approve.ReportApproveFlow)
+	total, e := flowOb.GetCountByCondition(cond, pars)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取审批流总数失败, Err:" + e.Error()
+		return
+	}
+	if total <= 0 {
+		page := paging.GetPaging(params.CurrentIndex, params.PageSize, total)
+		resp.Paging = page
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "获取成功"
+		br.Data = resp
+		return
+	}
+
+	// 分页列表
+	var startSize int
+	if params.PageSize <= 0 {
+		params.PageSize = utils.PageSize20
+	}
+	if params.CurrentIndex <= 0 {
+		params.CurrentIndex = 1
+	}
+	startSize = utils.StartIndex(params.CurrentIndex, params.PageSize)
+
+	list, e := flowOb.GetPageItemsByCondition(cond, pars, []string{}, orderRule, startSize, params.PageSize)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取审批流分页列表失败, Err:" + e.Error()
+		return
+	}
+
+	// 指标分类
+	cnClassifyIdName, enClassifyIdName := make(map[int]string), make(map[int]string)
+	cnClassify, e := models.GetAllClassify()
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取中文分类失败, Err: " + e.Error()
+		return
+	}
+	for _, v := range cnClassify {
+		cnClassifyIdName[v.Id] = v.ClassifyName
+	}
+	enClassify, e := models.GetEnglishClassifies()
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取英文分类失败, Err: " + e.Error()
+		return
+	}
+	enRootIdMap := make(map[int]int) // 英文分类的一级分类ID
+	for _, v := range enClassify {
+		enClassifyIdName[v.Id] = v.ClassifyName
+		enRootIdMap[v.Id] = v.RootId
+	}
+
+	for _, v := range list {
+		t := report_approve.FormatReportApproveFlow2Item(v)
+		if v.ReportType == report_approve.FlowReportTypeEnglish {
+			t.ReportClassify = fmt.Sprintf("%s/%s/%s/%s", report_approve.FlowReportTypeMap[v.ReportType], enClassifyIdName[enRootIdMap[v.ClassifySecondId]], enClassifyIdName[v.ClassifyFirstId], enClassifyIdName[v.ClassifySecondId])
+		} else {
+			t.ReportClassify = fmt.Sprintf("%s/%s/%s", report_approve.FlowReportTypeMap[v.ReportType], cnClassifyIdName[v.ClassifyFirstId], cnClassifyIdName[v.ClassifySecondId])
+		}
+		resp.List = append(resp.List, t)
+	}
+
+	page := paging.GetPaging(params.CurrentIndex, params.PageSize, total)
+	resp.Paging = page
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// Add
+// @Title 新增审批流
+// @Description 新增审批流
+// @Param	request	body report_approve.ReportApproveFlowAddReq true "type json string"
+// @Success 200 {object} report_approve.ReportApproveFlowDetailItem
+// @router /flow/add [post]
+func (this *ReportApproveFlowController) Add() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req report_approve.ReportApproveFlowAddReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数有误"
+		br.ErrMsg = "参数解析失败, Err: " + e.Error()
+		return
+	}
+	req.FlowName = strings.TrimSpace(req.FlowName)
+	if req.FlowName == "" {
+		br.Msg = "请输入审批流名称"
+		return
+	}
+	if len([]rune(req.FlowName)) > 20 {
+		br.Msg = "审批流名称最多输入20个字符"
+		return
+	}
+	reportTypes := []int{report_approve.FlowReportTypeChinese, report_approve.FlowReportTypeEnglish, report_approve.FlowReportTypeSmart}
+	if !utils.InArrayByInt(reportTypes, req.ReportType) {
+		br.Msg = "审批流报告类型有误"
+		br.ErrMsg = fmt.Sprintf("审批流报告类型有误, ReportType: %d", req.ReportType)
+		return
+	}
+	if req.ClassifyFirstId <= 0 || req.ClassifySecondId <= 0 {
+		br.Msg = "请选择报告分类"
+		return
+	}
+	if len(req.Nodes) <= 0 {
+		br.Msg = "请添加审批流程"
+		return
+	}
+	approveTypes := []int{report_approve.NodeApproveTypeRoll, report_approve.NodeApproveTypeAll, report_approve.NodeApproveTypeAny}
+	approveUserTypes := []string{report_approve.NodeUserTypeNormal, report_approve.NodeUserTypeRole}
+	for _, v := range req.Nodes {
+		if !utils.InArrayByInt(approveTypes, v.ApproveType) {
+			br.Msg = "审批流类型有误"
+			br.ErrMsg = fmt.Sprintf("审批流类型有误, ApproveType: %d", v.ApproveType)
+			return
+		}
+		for _, v2 := range v.Users {
+			if !utils.InArrayByStr(approveUserTypes, v2.UserType) {
+				br.Msg = "审批流用户类型有误"
+				br.ErrMsg = fmt.Sprintf("审批流用户类型有误, UserType: %d", v2.UserType)
+				return
+			}
+		}
+	}
+
+	// 审批流是否已存在
+	{
+		flowOb := new(report_approve.ReportApproveFlow)
+		existCond := fmt.Sprintf(` AND %s = ? AND %s = ? AND %s = ?`, report_approve.ReportApproveFlowCols.ReportType, report_approve.ReportApproveFlowCols.ClassifyFirstId, report_approve.ReportApproveFlowCols.ClassifySecondId)
+		existPars := make([]interface{}, 0)
+		existPars = append(existPars, req.ReportType, req.ClassifyFirstId, req.ClassifySecondId)
+		exist, e := flowOb.GetItemByCondition(existCond, existPars, "")
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取审批流是否已存在失败, Err: " + e.Error()
+			return
+		}
+		if exist != nil {
+			br.Msg = "该分类已有审批流, 请勿重复添加"
+			return
+		}
+	}
+
+	flowItem := new(report_approve.ReportApproveFlow)
+	flowItem.FlowName = req.FlowName
+	flowItem.ReportType = req.ReportType
+	flowItem.ClassifyFirstId = req.ClassifyFirstId
+	flowItem.ClassifySecondId = req.ClassifySecondId
+	flowItem.CurrVersion = 1
+	flowItem.CreateTime = time.Now().Local()
+	flowItem.ModifyTime = time.Now().Local()
+
+	nodeItems := make([]*report_approve.ReportApproveNode, 0)
+	for _, v := range req.Nodes {
+		n := new(report_approve.ReportApproveNode)
+		n.ApproveType = v.ApproveType
+		n.CurrVersion = flowItem.CurrVersion
+		j, _ := json.Marshal(v.Users)
+		n.Users = string(j)
+		n.CreateTime = time.Now().Local()
+		nodeItems = append(nodeItems, n)
+	}
+
+	// 新增审批流和节点
+	if e := flowItem.CreateFlowAndNodes(flowItem, nodeItems); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "新增审批流和节点失败, Err: " + e.Error()
+		return
+	}
+
+	// 返回详情
+	detail, e := report_approve.FormatFlowAndNodesItem2Detail(flowItem, nodeItems)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取审批详情失败, Err: " + e.Error()
+		return
+	}
+
+	// 更新审批对应的报告状态:未发布->待提交
+	go func() {
+		_ = services.FlowOperateResetReportState(flowItem.ReportType, flowItem.ClassifyFirstId, flowItem.ClassifySecondId, models.ReportStateUnpublished, models.ReportStateWaitSubmit)
+	}()
+
+	br.Data = detail
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// Edit
+// @Title 编辑审批流
+// @Description 编辑审批流
+// @Param	request	body report_approve.ReportApproveFlowEditReq true "type json string"
+// @Success 200 {object} report_approve.ReportApproveFlowDetailItem
+// @router /flow/edit [post]
+func (this *ReportApproveFlowController) Edit() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req report_approve.ReportApproveFlowEditReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数有误"
+		br.ErrMsg = "参数解析失败, Err: " + e.Error()
+		return
+	}
+	if req.ReportApproveFlowId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("参数有误, ReportApproveFlowId: %d", req.ReportApproveFlowId)
+		return
+	}
+	req.FlowName = strings.TrimSpace(req.FlowName)
+	if req.FlowName == "" {
+		br.Msg = "请输入审批流名称"
+		return
+	}
+	if len([]rune(req.FlowName)) > 20 {
+		br.Msg = "审批流名称最多输入20个字符"
+		return
+	}
+	reportTypes := []int{report_approve.FlowReportTypeChinese, report_approve.FlowReportTypeEnglish, report_approve.FlowReportTypeSmart}
+	if !utils.InArrayByInt(reportTypes, req.ReportType) {
+		br.Msg = "审批流报告类型有误"
+		br.ErrMsg = fmt.Sprintf("审批流报告类型有误, ReportType: %d", req.ReportType)
+		return
+	}
+	if req.ClassifyFirstId <= 0 || req.ClassifySecondId <= 0 {
+		br.Msg = "请选择报告分类"
+		return
+	}
+	if len(req.Nodes) <= 0 {
+		br.Msg = "请添加审批流程"
+		return
+	}
+	approveTypes := []int{report_approve.NodeApproveTypeRoll, report_approve.NodeApproveTypeAll, report_approve.NodeApproveTypeAny}
+	approveUserTypes := []string{report_approve.NodeUserTypeNormal, report_approve.NodeUserTypeRole}
+	for _, v := range req.Nodes {
+		if !utils.InArrayByInt(approveTypes, v.ApproveType) {
+			br.Msg = "审批流类型有误"
+			br.ErrMsg = fmt.Sprintf("审批流类型有误, ApproveType: %d", v.ApproveType)
+			return
+		}
+		for _, v2 := range v.Users {
+			if !utils.InArrayByStr(approveUserTypes, v2.UserType) {
+				br.Msg = "审批流用户类型有误"
+				br.ErrMsg = fmt.Sprintf("审批流用户类型有误, UserType: %d", v2.UserType)
+				return
+			}
+		}
+	}
+
+	flowOb := new(report_approve.ReportApproveFlow)
+	flowItem, e := flowOb.GetItemById(req.ReportApproveFlowId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "审批流已被删除, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取审批流信息失败, Err: " + e.Error()
+		return
+	}
+
+	// 审批流是否已存在
+	{
+		existCond := fmt.Sprintf(` AND %s = ? AND %s = ? AND %s = ? AND %s <> ?`, report_approve.ReportApproveFlowCols.ReportType, report_approve.ReportApproveFlowCols.ClassifyFirstId, report_approve.ReportApproveFlowCols.ClassifySecondId, report_approve.ReportApproveFlowCols.ReportApproveFlowId)
+		existPars := make([]interface{}, 0)
+		existPars = append(existPars, req.ReportType, req.ClassifyFirstId, req.ClassifySecondId, req.ReportApproveFlowId)
+		exist, e := flowOb.GetItemByCondition(existCond, existPars, "")
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			br.Msg = "操作失败"
+			br.ErrMsg = "获取审批流是否已存在失败, Err: " + e.Error()
+			return
+		}
+		if exist != nil {
+			br.Msg = "该分类已有审批流, 请勿重复添加"
+			return
+		}
+	}
+
+	// 校验审批流是否关联了进行中的审批
+	{
+		approvingOb := new(report_approve.ReportApprove)
+		approvingCond := fmt.Sprintf(` AND %s = ? AND %s = ? AND %s = ?`, report_approve.ReportApproveCols.FlowId, report_approve.ReportApproveCols.FlowVersion, report_approve.ReportApproveCols.State)
+		approvingPars := make([]interface{}, 0)
+		approvingPars = append(approvingPars, flowItem.ReportApproveFlowId, flowItem.CurrVersion, report_approve.ReportApproveStateApproving)
+		count, e := approvingOb.GetCountByCondition(approvingCond, approvingPars)
+		if e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = "获取审批流关联进行中的审批数失败. Err: " + e.Error()
+			return
+		}
+		if count > 0 {
+			br.Msg = "当前有未走完流程的报告,请走完流程后再做变更"
+			return
+		}
+	}
+
+	// 变更了报告分类时, 判断是否允许变更
+	if req.ReportType != flowItem.ReportType || req.ClassifyFirstId != flowItem.ClassifyFirstId || req.ClassifySecondId != flowItem.ClassifySecondId {
+		checkOk, e := services.CheckReportApproveFlowChange(flowItem.ReportType, flowItem.ClassifyFirstId, flowItem.ClassifySecondId)
+		if e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = "校验审批流是否可变更失败, Err: " + e.Error()
+			return
+		}
+		if !checkOk {
+			br.Msg = "当前有未走完流程的报告, 请走完流程后再做变更!"
+			return
+		}
+	}
+
+	flowItem.FlowName = req.FlowName
+	flowItem.ReportType = req.ReportType
+	flowItem.ClassifyFirstId = req.ClassifyFirstId
+	flowItem.ClassifySecondId = req.ClassifySecondId
+	flowItem.CurrVersion += 1
+	flowItem.ModifyTime = time.Now().Local()
+
+	nodeItems := make([]*report_approve.ReportApproveNode, 0)
+	for _, v := range req.Nodes {
+		n := new(report_approve.ReportApproveNode)
+		n.ApproveType = v.ApproveType
+		n.CurrVersion = flowItem.CurrVersion
+		j, _ := json.Marshal(v.Users)
+		n.Users = string(j)
+		n.CreateTime = time.Now().Local()
+		nodeItems = append(nodeItems, n)
+	}
+
+	// 更新审批流和节点
+	if e := flowItem.UpdateFlowAndNodes(flowItem, nodeItems); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "更新审批流和节点失败, Err: " + e.Error()
+		return
+	}
+
+	// 返回详情
+	detail, e := report_approve.FormatFlowAndNodesItem2Detail(flowItem, nodeItems)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取审批详情失败, Err: " + e.Error()
+		return
+	}
+	br.Data = detail
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// Detail
+// @Title 审批流详情
+// @Description 审批流详情
+// @Param   ReportApproveFlowId  query  int  true  "审批流ID"
+// @Success 200 {object} report_approve.ReportApproveFlowDetailItem
+// @router /flow/detail [get]
+func (this *ReportApproveFlowController) Detail() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	flowId, _ := this.GetInt("ReportApproveFlowId")
+	if flowId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("参数有误, ReportApproveFlowId: %d", flowId)
+		return
+	}
+
+	flowOb := new(report_approve.ReportApproveFlow)
+	flowItem, e := flowOb.GetItemById(flowId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "审批流已被删除, 请刷新页面"
+			return
+		}
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取审批流信息失败, Err: " + e.Error()
+		return
+	}
+
+	// 审批节点
+	nodeOb := new(report_approve.ReportApproveNode)
+	nodeCond := fmt.Sprintf(` AND %s = ? AND %s = ?`, report_approve.ReportApproveNodeCols.ReportApproveFlowId, report_approve.ReportApproveNodeCols.CurrVersion)
+	nodePars := make([]interface{}, 0)
+	nodePars = append(nodePars, flowItem.ReportApproveFlowId, flowItem.CurrVersion)
+	nodes, e := nodeOb.GetItemsByCondition(nodeCond, nodePars, []string{}, "")
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取审批节点失败, Err: " + e.Error()
+		return
+	}
+
+	detail, e := report_approve.FormatFlowAndNodesItem2Detail(flowItem, nodes)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取审批详情失败, Err: " + e.Error()
+		return
+	}
+	br.Data = detail
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// Remove
+// @Title 删除审批流
+// @Description 删除审批流
+// @Param	request	body report_approve.ReportApproveFlowRemoveReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /flow/remove [post]
+func (this *ReportApproveFlowController) Remove() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req report_approve.ReportApproveFlowRemoveReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数有误"
+		br.ErrMsg = "参数解析失败, Err: " + e.Error()
+		return
+	}
+	if req.ReportApproveFlowId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("参数有误, ReportApproveFlowId: %d", req.ReportApproveFlowId)
+		return
+	}
+
+	flowOb := new(report_approve.ReportApproveFlow)
+	flowItem, e := flowOb.GetItemById(req.ReportApproveFlowId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "审批流已被删除, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取审批流信息失败, Err: " + e.Error()
+		return
+	}
+
+	// 校验是否允许删除
+	checkOk, e := services.CheckReportApproveFlowChange(flowItem.ReportType, flowItem.ClassifyFirstId, flowItem.ClassifySecondId)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "校验审批流是否可变更失败, Err: " + e.Error()
+		return
+	}
+	if !checkOk {
+		br.Msg = "当前有未走完流程的报告, 请走完流程后再做删除!"
+		return
+	}
+
+	// 删除审批流, 保留审批节点, 历史审批回显会用得到
+	if e = flowItem.Del(); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "删除审批流失败, Err: " + e.Error()
+		return
+	}
+
+	// 更新审批对应的报告状态:待提交->未发布
+	go func() {
+		_ = services.FlowOperateResetReportState(flowItem.ReportType, flowItem.ClassifyFirstId, flowItem.ClassifySecondId, models.ReportStateWaitSubmit, models.ReportStateUnpublished)
+	}()
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// ReportClassifyTree
+// @Title 报告分类树
+// @Description 报告分类树
+// @Param   ReportApproveFlowId  query  int  false  "审批流ID"
+// @Success 200 {object} report_approve.ReportClassifyTreeItem
+// @router /report/classify_tree [get]
+func (this *ReportApproveFlowController) ReportClassifyTree() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	flowId, _ := this.GetInt("ReportApproveFlowId")
+
+	// 获取审批流信息, 用于查询分类是否可选
+	var flowKey string
+	if flowId > 0 {
+		flowOb := new(report_approve.ReportApproveFlow)
+		flowItem, e := flowOb.GetItemById(flowId)
+		if e != nil {
+			if e.Error() == utils.ErrNoRow() {
+				br.Msg = "审批流已被删除, 请刷新页面"
+				return
+			}
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取审批流信息失败, Err: " + e.Error()
+			return
+		}
+		flowKey = fmt.Sprintf("%d-%d-%d", flowItem.ReportType, flowItem.ClassifyFirstId, flowItem.ClassifySecondId)
+	}
+
+	// 获取审批流列表
+	flowOb := new(report_approve.ReportApproveFlow)
+	flows, e := flowOb.GetItemsByCondition(``, make([]interface{}, 0), []string{}, "")
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取审批流列表失败, Err: " + e.Error()
+		return
+	}
+	hasFlowMap := make(map[string]bool)
+	for _, v := range flows {
+		k := fmt.Sprintf("%d-%d-%d", v.ReportType, v.ClassifyFirstId, v.ClassifySecondId)
+		if k == flowKey {
+			// 当前审批流对应的分类标记为可选状态
+			continue
+		}
+		hasFlowMap[k] = true
+	}
+
+	resp, cnTree, smartTree, enTree := make([]*report_approve.ReportClassifyTreeItem, 0), make([]*report_approve.ReportClassifyTreeItem, 0), make([]*report_approve.ReportClassifyTreeItem, 0), make([]*report_approve.ReportClassifyTreeItem, 0)
+
+	var cnErr, enErr error
+	wg := sync.WaitGroup{}
+	wg.Add(2)
+
+	// 中文/智能研报分类
+	go func() {
+		defer func() {
+			wg.Done()
+		}()
+
+		classify, e := models.GetAllClassify()
+		if e != nil {
+			cnErr = fmt.Errorf("GetAllClassify err: %s", e.Error())
+			return
+		}
+		cnTree = services.GetReportClassifyTreeRecursive(classify, 0)
+		for _, v := range cnTree {
+			for _, v2 := range v.Children {
+				k := fmt.Sprintf("%d-%d-%d", report_approve.FlowReportTypeChinese, v.ClassifyId, v2.ClassifyId)
+				v2.HasFlow = hasFlowMap[k]
+			}
+		}
+
+		smartTree = services.GetReportClassifyTreeRecursive(classify, 0)
+		for _, v := range smartTree {
+			for _, v2 := range v.Children {
+				k := fmt.Sprintf("%d-%d-%d", report_approve.FlowReportTypeSmart, v.ClassifyId, v2.ClassifyId)
+				v2.HasFlow = hasFlowMap[k]
+			}
+		}
+	}()
+
+	// 英文研报分类
+	go func() {
+		defer func() {
+			wg.Done()
+		}()
+
+		classify, e := models.GetAllEnglishClassify()
+		if e != nil {
+			enErr = fmt.Errorf("GetAllEnglishClassify err: %s", e.Error())
+			return
+		}
+		enTree = services.GetReportClassifyTreeRecursive(classify, 0)
+		for _, v := range enTree {
+			for _, v2 := range v.Children {
+				k := fmt.Sprintf("%d-%d-%d", report_approve.FlowReportTypeEnglish, v.ClassifyId, v2.ClassifyId)
+				v2.HasFlow = hasFlowMap[k]
+			}
+		}
+	}()
+
+	wg.Wait()
+
+	if cnErr != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取中文分类失败, Err: " + cnErr.Error()
+		return
+	}
+	if enErr != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取英文分类失败, Err: " + enErr.Error()
+		return
+	}
+
+	resp = append(resp, &report_approve.ReportClassifyTreeItem{
+		ClassifyId:   report_approve.FlowReportTypeChinese,
+		ClassifyName: "研报列表",
+		Children:     cnTree,
+	}, &report_approve.ReportClassifyTreeItem{
+		ClassifyId:   report_approve.FlowReportTypeEnglish,
+		ClassifyName: "英文研报",
+		Children:     enTree,
+	}, &report_approve.ReportClassifyTreeItem{
+		ClassifyId:   report_approve.FlowReportTypeSmart,
+		ClassifyName: "智能研报",
+		Children:     smartTree,
+	})
+
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}

+ 257 - 25
controllers/smart_report/smart_report.go

@@ -4,11 +4,11 @@ import (
 	"encoding/json"
 	"eta/eta_api/controllers"
 	"eta/eta_api/models"
+	"eta/eta_api/models/report_approve"
 	"eta/eta_api/models/smart_report"
 	"eta/eta_api/models/system"
 	"eta/eta_api/services"
 	"eta/eta_api/services/data"
-	smartReportService "eta/eta_api/services/smart_report"
 	"eta/eta_api/utils"
 	"fmt"
 	"github.com/kgiannakakis/mp3duration/src/mp3duration"
@@ -78,6 +78,14 @@ func (this *SmartReportController) Add() {
 	}
 	stageMax += 1
 
+	// 根据审批开关及审批流判断当前报告状态
+	state, e := services.CheckReportCurrState(report_approve.FlowReportTypeSmart, req.ClassifyIdFirst, req.ClassifyIdSecond, models.ReportOperateAdd)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "校验报告当前状态失败, Err: " + e.Error()
+		return
+	}
+
 	item := new(smart_report.SmartReport)
 	item.AddType = req.AddType
 	item.ClassifyIdFirst = req.ClassifyIdFirst
@@ -93,14 +101,14 @@ func (this *SmartReportController) Add() {
 	item.AdminRealName = sysUser.RealName
 	item.LastModifyAdminId = sysUser.AdminId
 	item.LastModifyAdminName = sysUser.RealName
-	item.State = smart_report.SmartReportStateWaitPublish
+	item.State = state
 	item.CreateTime = time.Now().Local()
 	item.ModifyTime = time.Now().Local()
 	item.ContentModifyTime = time.Now().Local()
 	item.HeadImg = req.HeadImg
 	item.EndImg = req.EndImg
 	item.CanvasColor = req.CanvasColor
-	
+
 	// 继承报告
 	if req.AddType == 2 {
 		ob := new(smart_report.SmartReport)
@@ -136,7 +144,7 @@ func (this *SmartReportController) Add() {
 	recordItem := &models.ReportStateRecord{
 		ReportId:   item.SmartReportId,
 		ReportType: 2,
-		State:      smart_report.SmartReportStateWaitPublish,
+		State:      state,
 		AdminId:    this.SysUser.AdminId,
 		AdminName:  this.SysUser.AdminName,
 		CreateTime: time.Now(),
@@ -219,7 +227,7 @@ func (this *SmartReportController) Edit() {
 		br.ErrMsg = "获取研报失败, Err: " + e.Error()
 		return
 	}
-	if item.State == 2 {
+	if item.State == models.ReportStatePublished || item.State == models.ReportStatePass {
 		br.Msg = "报告已发布, 请取消发布后编辑"
 		return
 	}
@@ -253,7 +261,6 @@ func (this *SmartReportController) Edit() {
 	item.EndImg = req.EndImg
 	item.CanvasColor = req.CanvasColor
 	if contentModify {
-		//fmt.Println(contentModify)
 		item.LastModifyAdminId = sysUser.AdminId
 		item.LastModifyAdminName = sysUser.RealName
 		item.ContentModifyTime = time.Now().Local()
@@ -328,7 +335,7 @@ func (this *SmartReportController) Remove() {
 
 	// ES更新报告为未发布
 	go func() {
-		_ = smartReportService.SmartReportElasticUpsert(item.SmartReportId, 1)
+		_ = services.SmartReportElasticUpsert(item.SmartReportId, 1)
 	}()
 
 	br.Ret = 200
@@ -422,6 +429,13 @@ func (this *SmartReportController) Publish() {
 		br.Msg = "参数有误"
 		return
 	}
+	operate := 0
+	if req.PublishState == smart_report.SmartReportStateWaitPublish {
+		operate = models.ReportOperateCancelPublish
+	}
+	if req.PublishState == smart_report.SmartReportStatePublished {
+		operate = models.ReportOperatePublish
+	}
 
 	ob := new(smart_report.SmartReport)
 	item, e := ob.GetItemById(req.SmartReportId)
@@ -444,10 +458,18 @@ func (this *SmartReportController) Publish() {
 		return
 	}
 
+	// 根据审批开关及审批流判断当前报告状态
+	state, e := services.CheckReportCurrState(report_approve.FlowReportTypeSmart, item.ClassifyIdFirst, item.ClassifyIdSecond, operate)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "校验报告当前状态失败, Err: " + e.Error()
+		return
+	}
+
 	cols := []string{"State", "ModifyTime"}
-	item.State = req.PublishState
+	item.State = state
 	item.ModifyTime = time.Now().Local()
-	if req.PublishState == smart_report.SmartReportStatePublished {
+	if state == smart_report.SmartReportStatePublished {
 		cols = append(cols, "PublishTime")
 		item.PublishTime = time.Now().Local()
 
@@ -457,8 +479,9 @@ func (this *SmartReportController) Publish() {
 		queue.ReportCode = item.ReportCode
 		_ = utils.Rc.LPush(utils.CACHE_CREATE_REPORT_IMGPDF_QUEUE, queue)
 	}
+
 	// 取消发布时同时清除掉Img和PDF的文件地址
-	if req.PublishState == smart_report.SmartReportStateWaitPublish {
+	if state == smart_report.SmartReportStateWaitPublish {
 		cols = append(cols, "DetailImgUrl", "DetailPdfUrl")
 		item.DetailImgUrl = ""
 		item.DetailPdfUrl = ""
@@ -468,10 +491,11 @@ func (this *SmartReportController) Publish() {
 		br.ErrMsg = "更新研报失败, Err: " + e.Error()
 		return
 	}
+
 	recordItem := &models.ReportStateRecord{
 		ReportId:   req.SmartReportId,
 		ReportType: 2,
-		State:      req.PublishState,
+		State:      state,
 		AdminId:    this.SysUser.AdminId,
 		AdminName:  this.SysUser.AdminName,
 		CreateTime: time.Now(),
@@ -479,15 +503,24 @@ func (this *SmartReportController) Publish() {
 	go func() {
 		_, _ = models.AddReportStateRecord(recordItem)
 	}()
-	// 生成音频
-	if req.PublishState == smart_report.SmartReportStatePublished && item.VideoUrl == "" {
-		go smartReportService.SmartReportBuildVideoAndUpdate(item)
-	}
 
-	// ES更新报告
-	go func() {
-		_ = smartReportService.SmartReportElasticUpsert(item.SmartReportId, req.PublishState)
-	}()
+	if state == smart_report.SmartReportStatePublished {
+		// 生成音频
+		if item.VideoUrl == "" {
+			go services.SmartReportBuildVideoAndUpdate(item)
+		}
+
+		// ES更新报告
+		go func() {
+			_ = services.SmartReportElasticUpsert(item.SmartReportId, models.ReportStatePublished)
+		}()
+	}
+	if state == smart_report.SmartReportStateWaitPublish {
+		// ES更新报告
+		go func() {
+			_ = services.SmartReportElasticUpsert(item.SmartReportId, models.ReportStateUnpublished)
+		}()
+	}
 
 	br.Ret = 200
 	br.Success = true
@@ -573,6 +606,18 @@ func (this *SmartReportController) PrePublish() {
 		return
 	}
 
+	// 校验是否开启了审批流
+	opening, e := services.CheckReportOpenApprove(report_approve.FlowReportTypeSmart, item.ClassifyIdFirst, item.ClassifyIdSecond)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "校验报告是否开启审批流失败, Err: " + e.Error()
+		return
+	}
+	if opening {
+		br.Msg = "报告已开启审批流, 不可设置定时发布"
+		return
+	}
+
 	item.PrePublishTime = preTime
 	item.PreMsgSend = req.PreMsgSend
 	cols := []string{"PrePublishTime", "PreMsgSend"}
@@ -733,7 +778,7 @@ func (this *SmartReportController) SaveContent() {
 	for _, ad := range admins {
 		adminIdName[ad.AdminId] = ad.RealName
 	}
-	editing, e := smartReportService.UpdateSmartReportEditing(req.SmartReportId, 1, sysUser.AdminId, sysUser.RealName, adminIdName)
+	editing, e := services.UpdateSmartReportEditing(req.SmartReportId, 1, sysUser.AdminId, sysUser.RealName, adminIdName)
 	if e != nil {
 		br.Msg = e.Error()
 		return
@@ -842,7 +887,7 @@ func (this *SmartReportController) MarkEditStatus() {
 		adminIdName[ad.AdminId] = ad.RealName
 	}
 
-	data, e := smartReportService.UpdateSmartReportEditing(req.SmartReportId, req.Status, sysUser.AdminId, sysUser.RealName, adminIdName)
+	data, e := services.UpdateSmartReportEditing(req.SmartReportId, req.Status, sysUser.AdminId, sysUser.RealName, adminIdName)
 	if e != nil {
 		br.Msg = e.Error()
 		return
@@ -859,7 +904,7 @@ func (this *SmartReportController) MarkEditStatus() {
 // @Description 报告列表
 // @Param   PageSize			query	int		true	"每页数据条数"
 // @Param   CurrentIndex		query	int		true	"当前页页码"
-// @Param   TimeType			query	string	false	"筛选的时间类别: publish_time-发布时间, modify_time-更新时间"
+// @Param   TimeType			query	string	false	"筛选的时间类别: publish_time-发布时间, modify_time-更新时间, approve_time-审批时间"
 // @Param   StartDate			query   string  false	"开始时间"
 // @Param   EndDate				query   string  false	"结束时间"
 // @Param   Frequency			query   string  false	"频度"
@@ -906,7 +951,7 @@ func (this *SmartReportController) List() {
 	if params.TimeType == "" {
 		params.TimeType = "publish_time"
 	}
-	if params.TimeType != "publish_time" && params.TimeType != "modify_time" {
+	if params.TimeType != "publish_time" && params.TimeType != "modify_time" && params.TimeType != "approve_time" {
 		br.Msg = "请选择正确的时间类型"
 		return
 	}
@@ -982,7 +1027,7 @@ func (this *SmartReportController) List() {
 		"smart_report_id", "report_code", "classify_id_first", "classify_name_first", "classify_id_second", "classify_name_second", "add_type",
 		"title", "abstract", "author", "frequency", "stage", "video_url", "video_name", "video_play_seconds", "video_size", "detail_img_url", "detail_pdf_url",
 		"admin_id", "admin_real_name", "state", "publish_time", "pre_publish_time", "pre_msg_send", "msg_is_send", "msg_send_time", "create_time", "modify_time",
-		"last_modify_admin_id", "last_modify_admin_name", "content_modify_time", "pv", "uv", "head_img", "end_img", "canvas_color",
+		"last_modify_admin_id", "last_modify_admin_name", "content_modify_time", "pv", "uv", "head_img", "end_img", "canvas_color", "approve_time",
 	}
 	list, e := reportOB.GetPageItemsByCondition(condition, pars, fields, "", startSize, params.PageSize)
 	if e != nil {
@@ -1005,7 +1050,7 @@ func (this *SmartReportController) List() {
 
 	for _, v := range list {
 		item := smart_report.FormatSmartReport2Item(v)
-		mark, e := smartReportService.UpdateSmartReportEditing(v.SmartReportId, 2, sysUser.AdminId, sysUser.RealName, adminIdName)
+		mark, e := services.UpdateSmartReportEditing(v.SmartReportId, 2, sysUser.AdminId, sysUser.RealName, adminIdName)
 		if e != nil {
 			br.Msg = "获取失败"
 			br.ErrMsg = "查询编辑中标记失败, Err:" + e.Error()
@@ -1258,3 +1303,190 @@ func (this *SmartReportController) VoiceUpload() {
 	br.Data = resp
 	return
 }
+
+// SubmitApprove
+// @Title 提交审批
+// @Description 提交审批接口
+// @Param	request	body models.ReportSubmitApproveReq true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /approve/submit [post]
+func (this *SmartReportController) SubmitApprove() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		return
+	}
+	var req models.ReportSubmitApproveReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数有误"
+		br.ErrMsg = "参数解析失败, Err: " + e.Error()
+		return
+	}
+	reportId := req.ReportId
+	if reportId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("参数有误, ReportId: %d", req.ReportId)
+		return
+	}
+
+	reportOb := new(smart_report.SmartReport)
+	reportItem, e := reportOb.GetItemById(reportId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "报告已被删除, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取报告失败, Err: " + e.Error()
+		return
+	}
+
+	// 校验当前审批配置, 返回下一个状态
+	state, e := services.CheckReportCurrState(report_approve.FlowReportTypeSmart, reportItem.ClassifyIdFirst, reportItem.ClassifyIdSecond, models.ReportOperateSubmitApprove)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "校验报告状态失败, Err: " + e.Error()
+		return
+	}
+
+	// 下一个状态不为待审批时, 仅更新状态
+	if state != models.ReportStateWaitApprove {
+		reportItem.State = state
+		e = reportItem.Update([]string{"State"})
+		if e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = "更新报告状态失败, Err: " + e.Error()
+			return
+		}
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "操作成功"
+		return
+	}
+
+	// 提交审批
+	approveId, e := services.SubmitReportApprove(report_approve.FlowReportTypeSmart, reportItem.SmartReportId, reportItem.Title, reportItem.ClassifyIdFirst, reportItem.ClassifyIdSecond, sysUser.AdminId, sysUser.RealName)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "提交审批失败, Err: " + e.Error()
+		return
+	}
+	reportItem.ApproveId = approveId
+	reportItem.State = models.ReportStateWaitApprove
+	reportItem.ModifyTime = time.Now().Local()
+	e = reportItem.Update([]string{"ApproveId", "State", "ModifyTime"})
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "更新报告状态失败, Err: " + e.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// CancelApprove
+// @Title 撤销审批
+// @Description 撤销审批
+// @Param	request	body models.ReportCancelApproveReq true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /approve/cancel [post]
+func (this *SmartReportController) CancelApprove() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		return
+	}
+	var req models.ReportCancelApproveReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数有误"
+		br.ErrMsg = "参数解析失败, Err: " + e.Error()
+		return
+	}
+	reportId := req.ReportId
+	if reportId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("参数有误, ReportId: %d", req.ReportId)
+		return
+	}
+
+	reportOb := new(smart_report.SmartReport)
+	reportItem, e := reportOb.GetItemById(reportId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "报告已被删除, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取报告失败, Err: " + e.Error()
+		return
+	}
+
+	// 校验当前审批配置, 返回下一个状态
+	state, e := services.CheckReportCurrState(report_approve.FlowReportTypeSmart, reportItem.ClassifyIdFirst, reportItem.ClassifyIdSecond, models.ReportOperateCancelApprove)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "校验报告状态失败, Err: " + e.Error()
+		return
+	}
+
+	// 下一个状态不为待提交时, 仅更新状态
+	if state != models.ReportStateWaitSubmit {
+		reportItem.State = state
+		e = reportItem.Update([]string{"State"})
+		if e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = "更新报告状态失败, Err: " + e.Error()
+			return
+		}
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "操作成功"
+		return
+	}
+	//if reportItem.ApproveId <= 0 {
+	//	br.Msg = "报告审批不存在"
+	//	br.ErrMsg = fmt.Sprintf("报告审批不存在, ApproveId: %d", reportItem.ApproveId)
+	//	return
+	//}
+
+	// 撤销审批
+	e = services.CancelReportApprove(report_approve.FlowReportTypeSmart, reportItem.SmartReportId, reportItem.ApproveId, sysUser.AdminId, sysUser.RealName)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "撤销审批失败, Err: " + e.Error()
+		return
+	}
+	//reportItem.ApproveId = 0
+	//reportItem.State = models.ReportStateWaitSubmit
+	//reportItem.ModifyTime = time.Now().Local()
+	//e = reportItem.UpdateReport([]string{"ApproveId", "State", "ModifyTime"})
+	//if e != nil {
+	//	br.Msg = "操作失败"
+	//	br.ErrMsg = "更新报告状态失败, Err: " + e.Error()
+	//	return
+	//}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}

+ 11 - 12
controllers/sys_role.go

@@ -3,7 +3,6 @@ package controllers
 import (
 	"encoding/json"
 	"eta/eta_api/models"
-	"eta/eta_api/models/company"
 	"eta/eta_api/models/system"
 	"eta/eta_api/services"
 	"eta/eta_api/utils"
@@ -718,17 +717,17 @@ func (this *SysRoleController) SystemConfig() {
 	list = append(list, osc)
 
 	// 获取审批流设置
-	confKey := "approval_flow"
-	confTmp, e := company.GetConfigDetailByCode(confKey)
-	if e != nil {
-		br.Msg = "获取审批流配置失败"
-		br.ErrMsg = "获取审批流配置失败, Err: " + e.Error()
-		return
-	}
-	list = append(list, system.BusinessConf{
-		ConfKey: "ApprovalFlow",
-		ConfVal: confTmp.ConfigValue,
-	})
+	//confKey := "approval_flow"
+	//confTmp, e := company.GetConfigDetailByCode(confKey)
+	//if e != nil {
+	//	br.Msg = "获取审批流配置失败"
+	//	br.ErrMsg = "获取审批流配置失败, Err: " + e.Error()
+	//	return
+	//}
+	//list = append(list, system.BusinessConf{
+	//	ConfKey: "ApprovalFlow",
+	//	ConfVal: confTmp.ConfigValue,
+	//})
 
 	br.Data = list
 	br.Ret = 200

+ 20 - 6
models/business_conf.go

@@ -9,12 +9,19 @@ import (
 )
 
 const (
-	BusinessConfUseXf          = "UseXf"
-	BusinessConfXfAppid        = "XfAppid"
-	BusinessConfXfApiKey       = "XfApiKey"
-	BusinessConfXfApiSecret    = "XfApiSecret"
-	BusinessConfXfVcn          = "XfVcn"
-	BusinessConfEnPptCoverImgs = "EnPptCoverImgs"
+	BusinessConfUseXf             = "UseXf"
+	BusinessConfXfAppid           = "XfAppid"
+	BusinessConfXfApiKey          = "XfApiKey"
+	BusinessConfXfApiSecret       = "XfApiSecret"
+	BusinessConfXfVcn             = "XfVcn"
+	BusinessConfEnPptCoverImgs    = "EnPptCoverImgs"
+	BusinessConfIsReportApprove   = "IsReportApprove"
+	BusinessConfReportApproveType = "ReportApproveType"
+)
+
+const (
+	BusinessConfReportApproveTypeEta   = "eta"
+	BusinessConfReportApproveTypeOther = "other"
 )
 
 // BusinessConf 商户配置表
@@ -165,3 +172,10 @@ func UpdateBusinessConfMulti(items []BusinessConfUpdate) (err error) {
 	}
 	return
 }
+
+func GetBusinessConfByKey(key string) (item *BusinessConf, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT * FROM business_conf WHERE conf_key = ? LIMIT 1`)
+	err = o.Raw(sql, key).QueryRow(&item)
+	return
+}

+ 17 - 2
models/db.go

@@ -11,6 +11,7 @@ import (
 	"eta/eta_api/models/data_stat"
 	"eta/eta_api/models/eta_trial"
 	"eta/eta_api/models/ppt_english"
+	"eta/eta_api/models/report_approve"
 	"eta/eta_api/models/sandbox"
 	"eta/eta_api/models/semantic_analysis"
 	"eta/eta_api/models/smart_report"
@@ -174,6 +175,9 @@ func init() {
 	//初始化AI
 	initAi()
 
+	// 报告审批
+	initReportApprove()
+
 	// 初始化部分数据表变量(直接init会有顺序问题=_=!)
 	data_manage.InitEdbSourceVar()
 }
@@ -483,8 +487,8 @@ func initExcel() {
 // initSmartReport 智能研报相关表
 func initSmartReport() {
 	orm.RegisterModel(
-		new(smart_report.SmartReport),        // 智能研报主表
-		new(smart_report.SmartReportSaveLog), // 智能研报-保存记录表
+		new(smart_report.SmartReport),         // 智能研报主表
+		new(smart_report.SmartReportSaveLog),  // 智能研报-保存记录表
 		new(smart_report.SmartReportResource), // 智能研报-资源表
 	)
 }
@@ -506,3 +510,14 @@ func initAi() {
 		new(aimod.AiChat),
 	)
 }
+
+// initReportApprove 报告审批相关表
+func initReportApprove() {
+	orm.RegisterModel(
+		new(report_approve.ReportApprove),        // 审批表
+		new(report_approve.ReportApproveFlow),    // 审批流表
+		new(report_approve.ReportApproveNode),    // 审批节点表
+		new(report_approve.ReportApproveRecord),  // 审批记录表
+		new(report_approve.ReportApproveMessage), // 审批消息表
+	)
+}

+ 76 - 8
models/english_report.go

@@ -23,7 +23,7 @@ type EnglishReport struct {
 	Frequency          string    `description:"频度"`
 	CreateTime         string    `description:"创建时间"`
 	ModifyTime         time.Time `description:"修改时间"`
-	State              int       `description:"1:未发布,2:已发布"`
+	State              int       `description:"1:未发布;2:已发布;3-待提交;4-待审批;5-已驳回;6-已通过"`
 	PublishTime        time.Time `description:"发布时间"`
 	PrePublishTime     time.Time `description:"预发布时间"`
 	Stage              int       `description:"期数"`
@@ -42,6 +42,8 @@ type EnglishReport struct {
 	FromReportId       int       `description:"继承的报告ID(英文策略报告ID)"`
 	AdminId            int       `description:"创建者账号"`
 	AdminRealName      string    `description:"创建者姓名"`
+	ApproveTime        time.Time `description:"审批时间"`
+	ApproveId          int       `description:"审批ID"`
 }
 
 func GetEnglishReportStage(classifyIdFirst, classifyIdSecond int) (count int, err error) {
@@ -240,7 +242,7 @@ type EnglishReportList struct {
 	Frequency          string    `description:"频度"`
 	CreateTime         string    `description:"创建时间"`
 	ModifyTime         time.Time `description:"修改时间"`
-	State              int       `description:"1:未发布,2:已发布"`
+	State              int       `description:"1:未发布;2:已发布;3-待提交;4-待审批;5-已驳回;6-已通过"`
 	PublishTime        string    `description:"发布时间"`
 	PrePublishTime     string    `description:"预发布时间"`
 	Stage              int       `description:"期数"`
@@ -264,6 +266,7 @@ type EnglishReportList struct {
 	FullClassifyName   string    `description:"顶级分类名/父级分类名/当前分类名"`
 	ClassifyIdRoot     int       `description:"顶级分类id"`
 	ClassifyNameRoot   string    `description:"顶级分类名称"`
+	ApproveTime        string    `description:"审批时间"`
 }
 
 type EnglishReportListResp struct {
@@ -308,7 +311,8 @@ func GetEnglishReportList(condition string, pars []interface{}, companyType stri
 	if condition != "" {
 		sql += condition
 	}
-	sql += `ORDER BY state ASC, modify_time DESC LIMIT ?,?`
+	// 排序:1:未发布;2:已发布;3-待提交;4-待审批;5-已驳回;6-已通过
+	sql += `ORDER BY FIELD(state,3,1,4,5,6,2), modify_time DESC LIMIT ?,?`
 	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
 	return
 }
@@ -335,7 +339,7 @@ func GetEnglishReportCountByCondition(condition string, pars []interface{}) (cou
 	return
 }
 
-// 发布报告
+// PublishEnglishReportById 发布报告
 func PublishEnglishReportById(reportId int, publishTime string) (err error) {
 	o := orm.NewOrmUsingDB("rddp")
 	sql := `UPDATE english_report SET state=2,publish_time=?,pre_publish_time=null,modify_time=NOW() WHERE id = ? `
@@ -343,11 +347,19 @@ func PublishEnglishReportById(reportId int, publishTime string) (err error) {
 	return
 }
 
-// 取消发布报告
-func PublishCancelEnglishReport(reportIds int) (err error) {
+// ResetEnglishReportById 重置报告状态
+func ResetEnglishReportById(reportId, state int) (err error) {
 	o := orm.NewOrmUsingDB("rddp")
-	sql := ` UPDATE english_report SET state=1,pre_publish_time=null WHERE id =?  `
-	_, err = o.Raw(sql, reportIds).Exec()
+	sql := `UPDATE english_report SET state = ?, pre_publish_time = null, modify_time = NOW() WHERE id = ?`
+	_, err = o.Raw(sql, state, reportId).Exec()
+	return
+}
+
+// PublishCancelEnglishReport 取消发布报告
+func PublishCancelEnglishReport(reportIds, state int) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` UPDATE english_report SET state=?, pre_publish_time=null WHERE id =?  `
+	_, err = o.Raw(sql, state, reportIds).Exec()
 	return
 }
 
@@ -827,3 +839,59 @@ where a.id IN (` + utils.GetOrmInReplace(len(classifyIds)) + `)`
 	_, err = o.Raw(sql, classifyIds).QueryRows(&list)
 	return
 }
+
+func (m *EnglishReport) GetItemById(id int) (item *EnglishReport, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT * FROM english_report WHERE id = ? LIMIT 1`
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+// GetEnglishClassifies 获取所有英文分类
+func GetEnglishClassifies() (list []*EnglishClassify, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` SELECT * FROM english_classify `
+	_, err = o.Raw(sql).QueryRows(&list)
+	return
+}
+
+// GetEnglishReportStateCount 获取指定状态的报告数量
+func GetEnglishReportStateCount(state int) (count int, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT COUNT(1) AS count FROM english_report WHERE state = ?`
+	err = o.Raw(sql, state).QueryRow(&count)
+	return
+}
+
+// UpdateEnglishReportsStateByCond 批量更新报告状态
+func UpdateEnglishReportsStateByCond(classifyFirstId, classifySecondId, oldState, newState int) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	cond := ``
+	if classifyFirstId > 0 {
+		cond += fmt.Sprintf(` AND classify_id_first = %d`, classifyFirstId)
+	}
+	if classifySecondId > 0 {
+		cond += fmt.Sprintf(` AND classify_id_second = %d`, classifySecondId)
+	}
+	sql := fmt.Sprintf(`UPDATE english_report SET state = ?, pre_publish_time = NULL WHERE state = ? %s`, cond)
+	_, err = o.Raw(sql, newState, oldState).Exec()
+	return
+}
+
+// UpdateEnglishReportsStateBySecondIds 批量更新二级分类报告状态
+func UpdateEnglishReportsStateBySecondIds(oldState, newState int, secondIds []int) (err error) {
+	if len(secondIds) <= 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("rddp")
+	// (有审批流的)未发布->待提交
+	sql := fmt.Sprintf(`UPDATE english_report SET state = ?, pre_publish_time = NULL WHERE state = ? AND classify_id_second IN (%s)`, utils.GetOrmInReplace(len(secondIds)))
+	_, err = o.Raw(sql, newState, oldState, secondIds).Exec()
+	if err != nil {
+		return
+	}
+	// (无审批流的)待提交->未发布
+	sql = fmt.Sprintf(`UPDATE english_report SET state = ?, pre_publish_time = NULL WHERE state = ? AND classify_id_second NOT IN (%s)`, utils.GetOrmInReplace(len(secondIds)))
+	_, err = o.Raw(sql, oldState, newState, secondIds).Exec()
+	return
+}

+ 101 - 10
models/report.go

@@ -2,12 +2,33 @@ package models
 
 import (
 	"eta/eta_api/utils"
+	"fmt"
 	"github.com/beego/beego/v2/client/orm"
 	"github.com/rdlucklib/rdluck_tools/paging"
 	"strings"
 	"time"
 )
 
+// 报告状态
+const (
+	ReportStateUnpublished = 1 // 未发布
+	ReportStatePublished   = 2 // 已发布
+	ReportStateWaitSubmit  = 3 // 待提交
+	ReportStateWaitApprove = 4 // 审批中
+	ReportStateRefused     = 5 // 已驳回
+	ReportStatePass        = 6 // 已通过
+)
+
+// 报告操作
+const (
+	ReportOperateAdd           = 1 // 新增报告
+	ReportOperateEdit          = 2 // 编辑报告
+	ReportOperatePublish       = 3 // 发布报告
+	ReportOperateCancelPublish = 4 // 取消发布报告
+	ReportOperateSubmitApprove = 5 // 提交审批
+	ReportOperateCancelApprove = 6 // 撤回审批
+)
+
 type Report struct {
 	Id                 int       `orm:"column(id)" description:"报告Id"`
 	AddType            int       `description:"新增方式:1:新增报告,2:继承报告"`
@@ -21,7 +42,7 @@ type Report struct {
 	Frequency          string    `description:"频度"`
 	CreateTime         string    `description:"创建时间"`
 	ModifyTime         time.Time `description:"修改时间"`
-	State              int       `description:"1:未发布,2:已发布"`
+	State              int       `description:"1:未发布;2:已发布;3-待提交;4-待审批;5-已驳回;6-已通过"`
 	PublishTime        time.Time `description:"发布时间"`
 	Stage              int       `description:"期数"`
 	MsgIsSend          int       `description:"消息是否已发送,0:否,1:是"`
@@ -40,6 +61,8 @@ type Report struct {
 	MsgSendTime        time.Time `description:"模版消息发送时间"`
 	AdminId            int       `description:"创建者账号"`
 	AdminRealName      string    `description:"创建者姓名"`
+	ApproveTime        time.Time `description:"审批时间"`
+	ApproveId          int       `description:"审批ID"`
 }
 
 type ReportList struct {
@@ -55,7 +78,7 @@ type ReportList struct {
 	Frequency          string                    `description:"频度"`
 	CreateTime         string                    `description:"创建时间"`
 	ModifyTime         time.Time                 `description:"修改时间"`
-	State              int                       `description:"1:未发布,2:已发布 3:已驳回 4:已审批"`
+	State              int                       `description:"1:未发布;2:已发布;3-待提交;4-待审批;5-已驳回;6-已通过"`
 	PublishTime        string                    `description:"发布时间"`
 	PrePublishTime     string                    `description:"预发布时间"`
 	Stage              int                       `description:"期数"`
@@ -80,6 +103,7 @@ type ReportList struct {
 	Editor             string                    `description:"编辑人"`
 	AdminId            int                       `description:"创建者账号"`
 	AdminRealName      string                    `description:"创建者姓名"`
+	ApproveTime        string                    `description:"审批时间"`
 }
 
 type ReportListResp struct {
@@ -122,7 +146,8 @@ func GetReportList(condition string, pars []interface{}, companyType string, sta
 	if condition != "" {
 		sql += condition
 	}
-	sql += `ORDER BY FIELD(state,3,1,2,4), modify_time DESC LIMIT ?,?`
+	// 排序:1:未发布;2:已发布;3-待提交;4-待审批;5-已驳回;6-已通过
+	sql += `ORDER BY FIELD(state,3,1,4,5,6,2), modify_time DESC LIMIT ?,?`
 	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
 	return
 }
@@ -138,16 +163,16 @@ func PublishReport(reportIds []int) (err error) {
 	return
 }
 
-// PublishCancleReport 取消发布报告
-func PublishCancleReport(reportIds int, publishTimeNullFlag bool) (err error) {
+// PublishCancelReport 取消发布报告
+func PublishCancelReport(reportId, state int, publishTimeNullFlag bool) (err error) {
 	o := orm.NewOrmUsingDB("rddp")
 	var sql string
 	if publishTimeNullFlag {
-		sql = ` UPDATE report SET state=1, publish_time=null, pre_publish_time=null, pre_msg_send=0 WHERE id =?`
+		sql = ` UPDATE report SET state=?, publish_time=null, pre_publish_time=null, pre_msg_send=0 WHERE id =?`
 	} else {
-		sql = ` UPDATE report SET state=1, pre_publish_time=null, pre_msg_send=0 WHERE id =?`
+		sql = ` UPDATE report SET state=?, pre_publish_time=null, pre_msg_send=0 WHERE id =?`
 	}
-	_, err = o.Raw(sql, reportIds).Exec()
+	_, err = o.Raw(sql, state, reportId).Exec()
 	return
 }
 
@@ -674,7 +699,7 @@ SELECT DISTINCT report_id FROM report_chapter WHERE publish_state = 2 AND (video
 	return
 }
 
-// 发布报告
+// PublishReportById 发布报告
 func PublishReportById(reportId int, publishTime time.Time) (err error) {
 	o := orm.NewOrmUsingDB("rddp")
 	sql := `UPDATE report SET state = 2, publish_time = ?, pre_publish_time=null, pre_msg_send=0, modify_time = NOW() WHERE id = ? `
@@ -682,6 +707,14 @@ func PublishReportById(reportId int, publishTime time.Time) (err error) {
 	return
 }
 
+// ResetReportById 重置报告状态
+func ResetReportById(reportId, state int) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `UPDATE report SET state = ?, pre_publish_time = null, pre_msg_send = 0, modify_time = NOW() WHERE id = ?`
+	_, err = o.Raw(sql, state, reportId).Exec()
+	return
+}
+
 // GetCommentReportByReportId 查询有留言的报告列表
 func GetCommentReportByReportId(condition string, pars []interface{}, startSize, pageSize int) (list []*Report, err error) {
 	o := orm.NewOrmUsingDB("rddp")
@@ -1049,4 +1082,62 @@ func SetPrePublishReportById(reportId int, prePublishTime string, preMsgSend int
 	sql := `UPDATE report SET pre_publish_time=?, pre_msg_send=? WHERE id = ? and state = 1 `
 	_, err = o.Raw(sql, prePublishTime, preMsgSend, reportId).Exec()
 	return
-}
+}
+
+// ReportSubmitApproveReq 提交审批请求体
+type ReportSubmitApproveReq struct {
+	ReportId int `description:"报告ID"`
+}
+
+// ReportCancelApproveReq 撤回审批请求体
+type ReportCancelApproveReq struct {
+	ReportId int `description:"报告ID"`
+}
+
+func (m *Report) GetItemById(id int) (item *Report, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT * FROM report WHERE id = ? LIMIT 1`
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+// GetReportStateCount 获取指定状态的报告数量
+func GetReportStateCount(state int) (count int, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT COUNT(1) AS count FROM report WHERE state = ?`
+	err = o.Raw(sql, state).QueryRow(&count)
+	return
+}
+
+// UpdateReportsStateByCond 批量更新报告状态
+func UpdateReportsStateByCond(classifyFirstId, classifySecondId, oldState, newState int) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	cond := ``
+	if classifyFirstId > 0 {
+		cond += fmt.Sprintf(` AND classify_id_first = %d`, classifyFirstId)
+	}
+	if classifySecondId > 0 {
+		cond += fmt.Sprintf(` AND classify_id_second = %d`, classifySecondId)
+	}
+	sql := fmt.Sprintf(`UPDATE report SET state = ?, pre_publish_time = NULL WHERE state = ? %s`, cond)
+	_, err = o.Raw(sql, newState, oldState).Exec()
+	return
+}
+
+// UpdateReportsStateBySecondIds 批量更新二级分类报告状态
+func UpdateReportsStateBySecondIds(oldState, newState int, secondIds []int) (err error) {
+	if len(secondIds) <= 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("rddp")
+	// (有审批流的)未发布->待提交
+	sql := fmt.Sprintf(`UPDATE report SET state = ?, pre_publish_time = NULL WHERE state = ? AND classify_id_second IN (%s)`, utils.GetOrmInReplace(len(secondIds)))
+	_, err = o.Raw(sql, newState, oldState, secondIds).Exec()
+	if err != nil {
+		return
+	}
+	// (无审批流的)待提交->未发布
+	sql = fmt.Sprintf(`UPDATE report SET state = ?, pre_publish_time = NULL WHERE state = ? AND classify_id_second NOT IN (%s)`, utils.GetOrmInReplace(len(secondIds)))
+	_, err = o.Raw(sql, oldState, newState, secondIds).Exec()
+	return
+}

+ 35 - 0
models/report_approve/constant.go

@@ -0,0 +1,35 @@
+package report_approve
+
+// 报告类型
+const (
+	FlowReportTypeChinese = 1 // 中文研报
+	FlowReportTypeEnglish = 2 // 英文研报
+	FlowReportTypeSmart   = 3 // 智能研报
+)
+
+var FlowReportTypeMap = map[int]string{
+	FlowReportTypeChinese: "中文研报",
+	FlowReportTypeEnglish: "英文研报",
+	FlowReportTypeSmart:   "智能研报",
+}
+
+// 节点审批方式
+const (
+	NodeApproveTypeRoll = 1 // 依次审批
+	NodeApproveTypeAll  = 2 // 会签
+	NodeApproveTypeAny  = 3 // 或签
+)
+
+// 节点审批人类型
+const (
+	NodeUserTypeNormal = "user" // 用户
+	NodeUserTypeRole   = "role" // 角色
+)
+
+// 报告审批状态
+const (
+	ReportApproveStateApproving = 1 // 待审批
+	ReportApproveStatePass      = 2 // 已审批
+	ReportApproveStateRefuse    = 3 // 已驳回
+	ReportApproveStateCancel    = 4 // 已撤销
+)

+ 448 - 0
models/report_approve/report_approve.go

@@ -0,0 +1,448 @@
+package report_approve
+
+import (
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"strings"
+	"time"
+)
+
+// ReportApprove 报告审批表
+type ReportApprove struct {
+	ReportApproveId  int       `orm:"column(report_approve_id);pk" description:"审批ID"`
+	ReportType       int       `description:"报告类型:1-中文研报;2-英文研报;3-智能研报"`
+	ReportId         int       `description:"报告ID"`
+	ReportTitle      string    `description:"报告标题"`
+	ClassifyFirstId  int       `description:"一级分类ID"`
+	ClassifySecondId int       `description:"二级分类ID"`
+	State            int       `description:"审批状态:1-待审批;2-已审批;3-已驳回;4-已撤回"`
+	FlowId           int       `description:"审批流ID"`
+	FlowVersion      int       `description:"审批流版本"`
+	StartNodeId      int       `description:"开始节点ID"`
+	CurrNodeId       int       `description:"当前节点ID"`
+	ApplyUserId      int       `description:"申请人ID"`
+	ApplyUserName    string    `description:"申请人姓名"`
+	ApproveRemark    string    `description:"审批备注"`
+	ApproveTime      time.Time `description:"审批时间"`
+	CreateTime       time.Time `description:"创建时间"`
+	ModifyTime       time.Time `description:"修改时间"`
+}
+
+var ReportApproveCols = struct {
+	ReportApproveId  string
+	ReportType       string
+	ReportId         string
+	ReportTitle      string `description:"报告标题"`
+	ClassifyFirstId  string `description:"一级分类ID"`
+	ClassifySecondId string `description:"二级分类ID"`
+	State            string
+	FlowId           string
+	FlowVersion      string
+	StartNodeId      string
+	CurrNodeId       string
+	ApplyUserId      string `description:"申请人ID"`
+	ApplyUserName    string `description:"申请人姓名"`
+	ApproveRemark    string `description:"审批备注"`
+	ApproveTime      string `description:"审批时间"`
+	CreateTime       string
+	ModifyTime       string
+}{
+	ReportApproveId:  "report_approve_id",
+	ReportType:       "report_type",
+	ReportId:         "report_id",
+	ReportTitle:      "report_title",
+	ClassifyFirstId:  "classify_first_id",
+	ClassifySecondId: "classify_second_id",
+	State:            "state",
+	FlowId:           "flow_id",
+	FlowVersion:      "flow_version",
+	StartNodeId:      "start_node_id",
+	CurrNodeId:       "curr_node_id",
+	ApplyUserId:      "apply_user_id",
+	ApplyUserName:    "apply_user_name",
+	ApproveRemark:    "approve_remark",
+	ApproveTime:      "approve_time",
+	CreateTime:       "create_time",
+	ModifyTime:       "modify_time",
+}
+
+func (m *ReportApprove) TableName() string {
+	return "report_approve"
+}
+
+func (m *ReportApprove) PrimaryId() string {
+	return ReportApproveCols.ReportApproveId
+}
+
+func (m *ReportApprove) Create() (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	id, err := o.Insert(m)
+	if err != nil {
+		return
+	}
+	m.ReportApproveId = int(id)
+	return
+}
+
+func (m *ReportApprove) CreateMulti(items []*ReportApprove) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("rddp")
+	_, err = o.InsertMulti(len(items), items)
+	return
+}
+
+func (m *ReportApprove) Update(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	_, err = o.Update(m, cols...)
+	return
+}
+
+func (m *ReportApprove) Del() (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	_, err = o.Raw(sql, m.ReportApproveId).Exec()
+	return
+}
+
+func (m *ReportApprove) MultiDel(menuIds []int) (err error) {
+	if len(menuIds) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("rddp")
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s IN (%s)`, m.TableName(), m.PrimaryId(), utils.GetOrmInReplace(len(menuIds)))
+	_, err = o.Raw(sql, menuIds).Exec()
+	return
+}
+
+func (m *ReportApprove) GetItemById(id int) (item *ReportApprove, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+func (m *ReportApprove) GetItemByCondition(condition string, pars []interface{}, orderRule string) (item *ReportApprove, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	order := ``
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s %s LIMIT 1`, m.TableName(), condition, order)
+	err = o.Raw(sql, pars).QueryRow(&item)
+	return
+}
+
+func (m *ReportApprove) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM %s WHERE 1=1 %s`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+func (m *ReportApprove) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*ReportApprove, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+func (m *ReportApprove) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*ReportApprove, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s LIMIT ?,?`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+// ReportApproveItem 报告审批信息
+type ReportApproveItem struct {
+	ReportApproveId       int    `description:"审批ID"`
+	ReportApproveRecordId int    `description:"审批记录ID"`
+	ReportType            int    `description:"报告类型:1-中文研报;2-英文研报;3-智能研报"`
+	ReportId              int    `description:"报告ID"`
+	ReportTitle           string `description:"报告标题"`
+	ReportClassify        string `description:"报告分类"`
+	ClassifyFirstId       int    `description:"一级分类ID"`
+	ClassifySecondId      int    `description:"二级分类ID"`
+	State                 int    `description:"审批状态:1-待审批;2-已审批;3-已驳回;4-已撤回"`
+	RecordState           int    `description:"审批记录状态:1-待审批;2-已通过;3-已驳回"`
+	FlowId                int    `description:"审批流ID"`
+	FlowVersion           int    `description:"审批流版本"`
+	StartNodeId           int    `description:"开始节点ID"`
+	CurrNodeId            int    `description:"当前节点ID"`
+	ApplyUserId           int    `description:"申请人ID"`
+	ApplyUserName         string `description:"申请人姓名"`
+	ApproveRemark         string `description:"审批备注"`
+	ApproveTime           string `description:"审批时间"`
+	HandleTime            string `description:"处理时间"`
+	CreateTime            string `description:"创建时间"`
+	ModifyTime            string `description:"修改时间"`
+}
+
+// FormatReportApproveOrm2Item 格式化报告审批
+func FormatReportApproveOrm2Item(origin *ReportApproveItemOrm) (item *ReportApproveItem) {
+	item = new(ReportApproveItem)
+	if origin == nil {
+		return
+	}
+	item.ReportApproveId = origin.ReportApproveId
+	item.ReportApproveRecordId = origin.ReportApproveRecordId
+	item.ReportType = origin.ReportType
+	item.ReportId = origin.ReportId
+	item.ReportTitle = origin.ReportTitle
+	item.ClassifyFirstId = origin.ClassifyFirstId
+	item.ClassifySecondId = origin.ClassifySecondId
+	item.State = origin.State
+	item.RecordState = origin.RecordState
+	item.FlowId = origin.FlowId
+	item.FlowVersion = origin.FlowVersion
+	item.StartNodeId = origin.StartNodeId
+	item.CurrNodeId = origin.CurrNodeId
+	item.ApplyUserId = origin.ApplyUserId
+	item.ApplyUserName = origin.ApplyUserName
+	item.ApproveRemark = origin.ApproveRemark
+	item.ApproveTime = utils.TimeTransferString(utils.FormatDateTime, origin.ApproveTime)
+	item.HandleTime = utils.TimeTransferString(utils.FormatDateTime, origin.HandleTime)
+	item.CreateTime = utils.TimeTransferString(utils.FormatDateTime, origin.CreateTime)
+	item.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, origin.ModifyTime)
+	return
+}
+
+// ReportApproveListReq 审批列表请求体
+type ReportApproveListReq struct {
+	PageSize         int    `form:"PageSize"`
+	CurrentIndex     int    `form:"CurrentIndex"`
+	ListType         int    `form:"ListType" description:"列表类型:1-待处理;2-已处理;3-我发起的"`
+	ReportType       int    `form:"ReportType" description:"报告类型:1-中文研报;2-英文研报;3-智能研报"`
+	ClassifyFirstId  int    `form:"ClassifyFirstId" description:"一级分类ID"`
+	ClassifySecondId int    `form:"ClassifySecondId" description:"二级分类ID"`
+	Keyword          string `form:"Keyword" description:"关键词:报告标题"`
+	ApproveState     int    `form:"ApproveState" description:"审批状态:1-待审批;2-已审批;3-已驳回;4-已撤回"`
+	TimeType         int    `form:"TimeType" description:"时间类型:1-提交时间;2-处理时间;3-审批时间"`
+	StartTime        string `form:"StartTime" description:"开始时间"`
+	EndTime          string `form:"EndTime" description:"结束时间"`
+	SortField        int    `form:"SortField" description:"排序字段:1-提交时间;2-处理时间;3-审批时间"`
+	SortRule         int    `form:"SortRule" description:"排序方式: 1-正序; 2-倒序(默认)"`
+}
+
+// ReportApproveListResp 审批列表响应体
+type ReportApproveListResp struct {
+	List   []*ReportApproveItem
+	Paging *paging.PagingItem `description:"分页数据"`
+}
+
+type ReportApproveItemOrm struct {
+	ReportApproveId       int       `description:"审批ID"`
+	ReportApproveRecordId int       `description:"审批记录ID"`
+	ReportType            int       `description:"报告类型:1-中文研报;2-英文研报;3-智能研报"`
+	ReportId              int       `description:"报告ID"`
+	ReportTitle           string    `description:"报告标题"`
+	ClassifyFirstId       int       `description:"一级分类ID"`
+	ClassifySecondId      int       `description:"二级分类ID"`
+	State                 int       `description:"审批状态:1-待审批;2-已审批;3-已驳回;4-已撤回"`
+	RecordState           int       `description:"审批记录状态:1-待审批;2-已通过;3-已驳回"`
+	FlowId                int       `description:"审批流ID"`
+	FlowVersion           int       `description:"审批流版本"`
+	StartNodeId           int       `description:"开始节点ID"`
+	CurrNodeId            int       `description:"当前节点ID"`
+	ApplyUserId           int       `description:"申请人ID"`
+	ApplyUserName         string    `description:"申请人姓名"`
+	ApproveRemark         string    `description:"审批备注"`
+	ApproveTime           time.Time `description:"审批时间"`
+	HandleTime            time.Time `description:"处理时间"`
+	CreateTime            time.Time `description:"创建时间"`
+	ModifyTime            time.Time `description:"修改时间"`
+}
+
+// GetApprovingReportApproveCount 获取待处理的审批分页列表总数
+func GetApprovingReportApproveCount(cond string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	base := fmt.Sprintf(`SELECT a.report_approve_record_id
+		FROM report_approve_record AS a
+		JOIN report_approve AS b ON a.report_approve_id = b.report_approve_id AND a.node_id = b.curr_node_id
+		WHERE 1 = 1 %s`, cond)
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM (%s) t`, base)
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+// GetApprovingReportApprovePageList 获取待处理的审批列表-分页
+func GetApprovingReportApprovePageList(cond string, pars []interface{}, orderRule string, startSize, pageSize int) (items []*ReportApproveItemOrm, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	order := `ORDER BY a.create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT a.report_approve_record_id, a.state AS record_state, b.*
+		FROM report_approve_record AS a
+		JOIN report_approve AS b ON a.report_approve_id = b.report_approve_id AND a.node_id = b.curr_node_id
+		WHERE 1 = 1 %s %s
+		LIMIT ?,?`, cond, order)
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+// GetApprovedReportApproveCount 获取已处理的审批分页列表总数
+func GetApprovedReportApproveCount(cond string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	base := fmt.Sprintf(`SELECT a.report_approve_record_id
+		FROM report_approve_record AS a
+		JOIN report_approve AS b ON a.report_approve_id = b.report_approve_id
+		WHERE 1 = 1 %s`, cond)
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM (%s) t`, base)
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+// GetApprovedReportApprovePageList 获取已处理的审批列表-分页
+func GetApprovedReportApprovePageList(cond string, pars []interface{}, orderRule string, startSize, pageSize int) (items []*ReportApproveItemOrm, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	order := `ORDER BY a.create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT a.report_approve_record_id, a.state AS record_state, a.approve_time AS handle_time, b.*
+		FROM report_approve_record AS a
+		JOIN report_approve AS b ON a.report_approve_id = b.report_approve_id
+		WHERE 1 = 1 %s %s
+		LIMIT ?,?`, cond, order)
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+// GetApplyReportApproveCount 获取我发起的审批分页列表总数
+func GetApplyReportApproveCount(cond string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	base := fmt.Sprintf(`SELECT a.* FROM report_approve AS a WHERE 1 = 1 %s`, cond)
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM (%s) t`, base)
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+// GetApplyReportApprovePageList 获取我发起的审批列表-分页
+func GetApplyReportApprovePageList(cond string, pars []interface{}, orderRule string, startSize, pageSize int) (items []*ReportApproveItemOrm, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	order := `ORDER BY a.create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT a.* FROM report_approve AS a WHERE 1 = 1 %s %s LIMIT ?,?`, cond, order)
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+// ReportApproveDetail 审批详情信息
+type ReportApproveDetail struct {
+	Report           *ReportApproveDetailReport  `description:"报告信息"`
+	Approve          *ReportApproveDetailItem    `description:"审批信息"`
+	ApproveFlowNodes []*ReportApproveDetailNodes `description:"审批节点信息"`
+	//ApproveFlow      *ReportApproveFlowItem      `description:"审批流信息"`
+}
+
+// ReportApproveDetailItem 审批详情-审批信息
+type ReportApproveDetailItem struct {
+	ReportApproveId int    `description:"审批ID"`
+	State           int    `description:"审批状态:1-待审批;2-已审批;3-已驳回;4-已撤回"`
+	FlowId          int    `description:"审批流ID"`
+	FlowVersion     int    `description:"审批流版本"`
+	StartNodeId     int    `description:"开始节点ID"`
+	CurrNodeId      int    `description:"当前节点ID"`
+	ApplyUserId     int    `description:"申请人ID"`
+	ApplyUserName   string `description:"申请人姓名"`
+	ApproveTime     string `description:"审批时间"`
+	CreateTime      string `description:"创建时间"`
+	ModifyTime      string `description:"修改时间"`
+}
+
+// ReportApproveDetailReport 审批详情-报告信息
+type ReportApproveDetailReport struct {
+	ReportType     int    `description:"报告类型:1-中文研报;2-英文研报;3-智能研报"`
+	ReportId       int    `description:"报告ID"`
+	ReportTitle    string `description:"报告标题"`
+	ReportClassify string `description:"报告分类"`
+	//ClassifyFirstId  int    `description:"一级分类ID"`
+	//ClassifySecondId int    `description:"二级分类ID"`
+	//Content          string `description:"报告内容"`
+}
+
+// CreateApproveAndRecord 新增审批和记录
+func (m *ReportApprove) CreateApproveAndRecord(approveItem *ReportApprove, recordItems []*ReportApproveRecord) (err error) {
+	if approveItem == nil {
+		err = fmt.Errorf("approve is nil")
+		return
+	}
+	o := orm.NewOrmUsingDB("rddp")
+	tx, e := o.Begin()
+	if e != nil {
+		err = fmt.Errorf("orm begin err: %s", e.Error())
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = tx.Rollback()
+			return
+		}
+		_ = tx.Commit()
+	}()
+
+	lastId, e := tx.Insert(approveItem)
+	if e != nil {
+		err = fmt.Errorf("insert approve err: %s", e.Error())
+		return
+	}
+	approveItem.ReportApproveId = int(lastId)
+
+	if len(recordItems) > 0 {
+		for _, v := range recordItems {
+			v.ReportApproveId = approveItem.ReportApproveId
+		}
+		_, e = tx.InsertMulti(len(recordItems), recordItems)
+		if e != nil {
+			err = fmt.Errorf("insert records err: %s", e.Error())
+			return
+		}
+	}
+	return
+}
+
+// ReportApprovePassReq 审批通过请求体
+type ReportApprovePassReq struct {
+	ReportApproveId int `description:"审批ID"`
+}
+
+// ReportApproveRefuseReq 审批驳回请求体
+type ReportApproveRefuseReq struct {
+	ReportApproveId int    `description:"审批ID"`
+	ApproveRemark   string `description:"驳回理由"`
+}
+
+// ReportApproveCancelReq 撤销审批请求体
+type ReportApproveCancelReq struct {
+	ReportApproveId int `description:"审批ID"`
+}
+
+// ReportApproveCheckApproveOpenReq 校验分类是否打开审批请求体
+type ReportApproveCheckApproveOpenReq struct {
+	ReportType       int `description:"报告类型:1-中文研报;2-英文研报;3-智能研报"`
+	ClassifyFirstId  int `description:"一级分类ID"`
+	ClassifySecondId int `description:"二级分类ID"`
+}

+ 387 - 0
models/report_approve/report_approve_flow.go

@@ -0,0 +1,387 @@
+package report_approve
+
+import (
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"strings"
+	"time"
+)
+
+// ReportApproveFlow 报告审批流表
+type ReportApproveFlow struct {
+	ReportApproveFlowId int       `orm:"column(report_approve_flow_id);pk" description:"审批流ID"`
+	FlowName            string    `description:"审批流名称"`
+	ReportType          int       `description:"报告类型:1-中文研报;2-英文研报;3-智能研报"`
+	ClassifyFirstId     int       `description:"一级分类ID"`
+	ClassifySecondId    int       `description:"二级分类ID"`
+	CurrVersion         int       `description:"当前版本号"`
+	CreateTime          time.Time `description:"创建时间"`
+	ModifyTime          time.Time `description:"修改时间"`
+}
+
+var ReportApproveFlowCols = struct {
+	ReportApproveFlowId string
+	FlowName            string
+	ReportType          string
+	ClassifyFirstId     string
+	ClassifySecondId    string
+	CurrVersion         string
+	CreateTime          string
+	ModifyTime          string
+}{
+	ReportApproveFlowId: "report_approve_flow_id",
+	FlowName:            "flow_name",
+	ReportType:          "report_type",
+	ClassifyFirstId:     "classify_first_id",
+	ClassifySecondId:    "classify_second_id",
+	CurrVersion:         "curr_version",
+	CreateTime:          "create_time",
+	ModifyTime:          "modify_time",
+}
+
+func (m *ReportApproveFlow) TableName() string {
+	return "report_approve_flow"
+}
+
+func (m *ReportApproveFlow) PrimaryId() string {
+	return ReportApproveFlowCols.ReportApproveFlowId
+}
+
+func (m *ReportApproveFlow) Create() (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	id, err := o.Insert(m)
+	if err != nil {
+		return
+	}
+	m.ReportApproveFlowId = int(id)
+	return
+}
+
+func (m *ReportApproveFlow) CreateMulti(items []*ReportApproveFlow) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("rddp")
+	_, err = o.InsertMulti(len(items), items)
+	return
+}
+
+func (m *ReportApproveFlow) Update(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	_, err = o.Update(m, cols...)
+	return
+}
+
+func (m *ReportApproveFlow) Del() (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	_, err = o.Raw(sql, m.ReportApproveFlowId).Exec()
+	return
+}
+
+func (m *ReportApproveFlow) MultiDel(menuIds []int) (err error) {
+	if len(menuIds) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("rddp")
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s IN (%s)`, m.TableName(), m.PrimaryId(), utils.GetOrmInReplace(len(menuIds)))
+	_, err = o.Raw(sql, menuIds).Exec()
+	return
+}
+
+func (m *ReportApproveFlow) GetItemById(id int) (item *ReportApproveFlow, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+func (m *ReportApproveFlow) GetItemByCondition(condition string, pars []interface{}, orderRule string) (item *ReportApproveFlow, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	order := ``
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s %s LIMIT 1`, m.TableName(), condition, order)
+	err = o.Raw(sql, pars).QueryRow(&item)
+	return
+}
+
+func (m *ReportApproveFlow) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM %s WHERE 1=1 %s`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+func (m *ReportApproveFlow) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*ReportApproveFlow, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+func (m *ReportApproveFlow) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*ReportApproveFlow, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s LIMIT ?,?`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+// ReportApproveFlowItem 报告审批流信息
+type ReportApproveFlowItem struct {
+	ReportApproveFlowId int    `description:"审批流ID"`
+	FlowName            string `description:"审批流名称"`
+	ReportType          int    `description:"报告类型:1-中文研报;2-英文研报;3-智能研报"`
+	ClassifyFirstId     int    `description:"一级分类ID"`
+	ClassifySecondId    int    `description:"二级分类ID"`
+	ReportClassify      string `description:"报告类型: XX研报/一级分类/二级分类"`
+	CreateTime          string `description:"创建时间"`
+	ModifyTime          string `description:"修改时间"`
+}
+
+// FormatReportApproveFlow2Item 格式化报告审批流
+func FormatReportApproveFlow2Item(origin *ReportApproveFlow) (item *ReportApproveFlowItem) {
+	item = new(ReportApproveFlowItem)
+	if origin == nil {
+		return
+	}
+	item.ReportApproveFlowId = origin.ReportApproveFlowId
+	item.FlowName = origin.FlowName
+	item.ReportType = origin.ReportType
+	item.ClassifyFirstId = origin.ClassifyFirstId
+	item.ClassifySecondId = origin.ClassifySecondId
+	item.CreateTime = utils.TimeTransferString(utils.FormatDateTime, origin.CreateTime)
+	item.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, origin.ModifyTime)
+	return
+}
+
+// ReportApproveFlowAddReq 新增报告审批流请求体
+type ReportApproveFlowAddReq struct {
+	FlowName         string                     `description:"审批流名称"`
+	ReportType       int                        `description:"报告类型:1-中文研报;2-英文研报;3-智能研报"`
+	ClassifyFirstId  int                        `description:"一级分类ID"`
+	ClassifySecondId int                        `description:"二级分类ID"`
+	Nodes            []ReportApproveNodeSaveReq `description:"审批节点信息"`
+}
+
+// ReportApproveFlowEditReq 编辑报告审批流请求体
+type ReportApproveFlowEditReq struct {
+	ReportApproveFlowId int `description:"审批流ID"`
+	ReportApproveFlowAddReq
+}
+
+// ReportApproveFlowRemoveReq 删除报告审批流请求体
+type ReportApproveFlowRemoveReq struct {
+	ReportApproveFlowId int `description:"报告审批流ID"`
+}
+
+type ReportClassifyTreeItem struct {
+	ClassifyId   int                       `description:"分类ID"`
+	ClassifyName string                    `description:"分类名称"`
+	ParentId     int                       `description:"父级ID"`
+	HasFlow      bool                      `description:"是否已配置审批流"`
+	Children     []*ReportClassifyTreeItem `description:"子分类"`
+}
+
+// CreateFlowAndNodes 新增审批流和节点
+func (m *ReportApproveFlow) CreateFlowAndNodes(flowItem *ReportApproveFlow, nodeItems []*ReportApproveNode) (err error) {
+	if flowItem == nil {
+		err = fmt.Errorf("flow is nil")
+		return
+	}
+	o := orm.NewOrmUsingDB("rddp")
+	tx, e := o.Begin()
+	if e != nil {
+		err = fmt.Errorf("orm begin err: %s", e.Error())
+		return
+	}
+	prevNodes := make([]*ReportApproveNode, 0)
+	defer func() {
+		if err != nil {
+			_ = tx.Rollback()
+			return
+		}
+		_ = tx.Commit()
+		// 更新每个节点的下一个节点信息, 放在事务中会更新失败
+		if e = m.UpdateNextNodes(prevNodes); e != nil {
+			err = fmt.Errorf("UpdatePrevNodes err: %s", e.Error())
+			return
+		}
+	}()
+
+	lastId, e := tx.Insert(flowItem)
+	if e != nil {
+		err = fmt.Errorf("insert flow err: %s", e.Error())
+		return
+	}
+	flowItem.ReportApproveFlowId = int(lastId)
+
+	nodesLen := len(nodeItems)
+	if nodesLen == 0 {
+		return
+	}
+	prevId := 0
+	prevNode := new(ReportApproveNode)
+	for k, v := range nodeItems {
+		v.ReportApproveFlowId = flowItem.ReportApproveFlowId
+		v.PrevNodeId = prevId
+		id, e := tx.Insert(v)
+		if e != nil {
+			err = fmt.Errorf("insert node err: %s", e.Error())
+			return
+		}
+		v.ReportApproveNodeId = int(id)
+		prevId = v.ReportApproveNodeId
+
+		// 下一个节点
+		if prevNode != nil && k > 0 && k < nodesLen {
+			prevNode.NextNodeId = int(id)
+			prevNodes = append(prevNodes, prevNode)
+		}
+		prevNode = v
+	}
+	return
+}
+
+func (m *ReportApproveFlow) UpdateNextNodes(nodes []*ReportApproveNode) (err error) {
+	if len(nodes) == 0 {
+		return
+	}
+	updateCols := []string{"NextNodeId"}
+	for _, v := range nodes {
+		e := v.Update(updateCols)
+		if e != nil {
+			err = fmt.Errorf("prev node update err: %s", e.Error())
+			return
+		}
+	}
+	return
+}
+
+// UpdateFlowAndNodes 更新审批流和节点
+func (m *ReportApproveFlow) UpdateFlowAndNodes(flowItem *ReportApproveFlow, nodeItems []*ReportApproveNode) (err error) {
+	if flowItem == nil {
+		err = fmt.Errorf("flow is nil")
+		return
+	}
+	o := orm.NewOrmUsingDB("rddp")
+	tx, e := o.Begin()
+	if e != nil {
+		err = fmt.Errorf("orm begin err: %s", e.Error())
+		return
+	}
+	prevNodes := make([]*ReportApproveNode, 0)
+	defer func() {
+		if err != nil {
+			_ = tx.Rollback()
+			return
+		}
+		_ = tx.Commit()
+		// 更新每个节点的下一个节点信息, 放在事务中会更新失败
+		if e = m.UpdateNextNodes(prevNodes); e != nil {
+			err = fmt.Errorf("UpdatePrevNodes err: %s", e.Error())
+			return
+		}
+	}()
+
+	// 更新审批流
+	updateCols := []string{"FlowName", "ReportType", "ClassifyFirstId", "ClassifySecondId", "CurrVersion", "ModifyTime"}
+	if e = flowItem.Update(updateCols); e != nil {
+		err = fmt.Errorf("update flow err: %s", e.Error())
+		return
+	}
+
+	// 新增节点(旧版的节点不删除, 根据版本号区分)
+	nodesLen := len(nodeItems)
+	if nodesLen == 0 {
+		return
+	}
+	prevId := 0
+	prevNode := new(ReportApproveNode)
+	for k, v := range nodeItems {
+		v.ReportApproveFlowId = flowItem.ReportApproveFlowId
+		v.PrevNodeId = prevId
+		id, e := tx.Insert(v)
+		if e != nil {
+			err = fmt.Errorf("insert node err: %s", e.Error())
+			return
+		}
+		v.ReportApproveNodeId = int(id)
+		prevId = v.ReportApproveNodeId
+
+		// 下一个节点
+		if prevNode != nil && k > 0 && k < nodesLen {
+			prevNode.NextNodeId = int(id)
+			prevNodes = append(prevNodes, prevNode)
+		}
+		prevNode = v
+	}
+	return
+}
+
+// ReportApproveFlowDetailItem 报告审批流详情信息
+type ReportApproveFlowDetailItem struct {
+	ReportApproveFlowItem `description:"审批流信息"`
+	Nodes                 []*ReportApproveNodeItem `description:"节点信息"`
+}
+
+// FormatFlowAndNodesItem2Detail 格式化审批流详情
+func FormatFlowAndNodesItem2Detail(flowItem *ReportApproveFlow, nodeItems []*ReportApproveNode) (detail *ReportApproveFlowDetailItem, err error) {
+	if flowItem == nil {
+		return
+	}
+	detail = new(ReportApproveFlowDetailItem)
+	detail.ReportApproveFlowId = flowItem.ReportApproveFlowId
+	detail.FlowName = flowItem.FlowName
+	detail.ReportType = flowItem.ReportType
+	detail.ClassifyFirstId = flowItem.ClassifyFirstId
+	detail.ClassifySecondId = flowItem.ClassifySecondId
+	detail.CreateTime = utils.TimeTransferString(utils.FormatDateTime, flowItem.CreateTime)
+	detail.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, flowItem.ModifyTime)
+	detail.Nodes = make([]*ReportApproveNodeItem, 0)
+	for _, v := range nodeItems {
+		t, e := FormatReportApproveNode2Item(v)
+		if e != nil {
+			err = fmt.Errorf("format node err: %s", e.Error())
+			return
+		}
+		detail.Nodes = append(detail.Nodes, t)
+	}
+	return
+}
+
+// ReportApproveFlowListReq 审批流列表请求体
+type ReportApproveFlowListReq struct {
+	PageSize         int    `form:"PageSize"`
+	CurrentIndex     int    `form:"CurrentIndex"`
+	ReportType       int    `form:"ReportType" description:"报告类型:1-中文研报;2-英文研报;3-智能研报"`
+	ClassifyFirstId  int    `form:"ClassifyFirstId" description:"一级分类ID"`
+	ClassifySecondId int    `form:"ClassifySecondId" description:"二级分类ID"`
+	Keyword          string `form:"Keyword" description:"关键词"`
+	SortRule         int    `form:"SortRule" description:"排序方式: 1-正序; 2-倒序(默认)"`
+}
+
+// ReportApproveFlowListResp 审批流列表响应体
+type ReportApproveFlowListResp struct {
+	List   []*ReportApproveFlowItem
+	Paging *paging.PagingItem `description:"分页数据"`
+}

+ 204 - 0
models/report_approve/report_approve_message.go

@@ -0,0 +1,204 @@
+package report_approve
+
+import (
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"strings"
+	"time"
+)
+
+// ReportApproveMessage 报告审批消息表
+type ReportApproveMessage struct {
+	Id              int       `orm:"column(id);pk"`
+	SendUserId      int       `description:"发送人ID"`
+	ReceiveUserId   int       `description:"接收者ID"`
+	Content         string    `description:"消息内容"`
+	Remark          string    `description:"备注信息"`
+	ReportApproveId int       `description:"审批ID"`
+	ApproveState    int       `description:"审批状态:1-待审批;2-已审批;3-已驳回;4-已撤回"`
+	IsRead          int       `description:"是否已读:0-未读;1-已读"`
+	CreateTime      time.Time `description:"创建时间"`
+	ModifyTime      time.Time `description:"修改时间"`
+}
+
+var ReportApproveMessageCols = struct {
+	Id              string
+	SendUserId      string
+	ReceiveUserId   string
+	Content         string
+	Remark          string
+	ReportApproveId string
+	ApproveState    string
+	IsRead          string
+	CreateTime      string
+	ModifyTime      string
+}{
+	Id:              "id",
+	SendUserId:      "send_user_id",
+	ReceiveUserId:   "receive_user_id",
+	Content:         "content",
+	Remark:          "remark",
+	ReportApproveId: "report_approve_id",
+	ApproveState:    "approve_state",
+	IsRead:          "is_read",
+	CreateTime:      "create_time",
+	ModifyTime:      "modify_time",
+}
+
+func (m *ReportApproveMessage) TableName() string {
+	return "report_approve_message"
+}
+
+func (m *ReportApproveMessage) PrimaryId() string {
+	return ReportApproveMessageCols.Id
+}
+
+func (m *ReportApproveMessage) Create() (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	id, err := o.Insert(m)
+	if err != nil {
+		return
+	}
+	m.Id = int(id)
+	return
+}
+
+func (m *ReportApproveMessage) CreateMulti(items []*ReportApproveMessage) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("rddp")
+	_, err = o.InsertMulti(len(items), items)
+	return
+}
+
+func (m *ReportApproveMessage) Update(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	_, err = o.Update(m, cols...)
+	return
+}
+
+func (m *ReportApproveMessage) Del() (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	_, err = o.Raw(sql, m.Id).Exec()
+	return
+}
+
+func (m *ReportApproveMessage) MultiDel(menuIds []int) (err error) {
+	if len(menuIds) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("rddp")
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s IN (%s)`, m.TableName(), m.PrimaryId(), utils.GetOrmInReplace(len(menuIds)))
+	_, err = o.Raw(sql, menuIds).Exec()
+	return
+}
+
+func (m *ReportApproveMessage) GetItemById(id int) (item *ReportApproveMessage, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+func (m *ReportApproveMessage) GetItemByCondition(condition string, pars []interface{}, orderRule string) (item *ReportApproveMessage, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	order := ``
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s %s LIMIT 1`, m.TableName(), condition, order)
+	err = o.Raw(sql, pars).QueryRow(&item)
+	return
+}
+
+func (m *ReportApproveMessage) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM %s WHERE 1=1 %s`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+func (m *ReportApproveMessage) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*ReportApproveMessage, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+func (m *ReportApproveMessage) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*ReportApproveMessage, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s LIMIT ?,?`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+// ReportApproveMessageItem 报告审批消息信息
+type ReportApproveMessageItem struct {
+	Id              int
+	SendUserId      int    `description:"发送人ID"`
+	ReceiveUserId   int    `description:"接收者ID"`
+	Content         string `description:"消息内容"`
+	Remark          string `description:"备注信息"`
+	ReportApproveId int    `description:"审批ID"`
+	ApproveState    int    `description:"审批状态:1-待审批;2-已审批;3-已驳回;4-已撤回"`
+	IsRead          int    `description:"是否已读:0-未读;1-已读"`
+	CreateTime      string `description:"创建时间"`
+	ModifyTime      string `description:"修改时间"`
+}
+
+// FormatReportApproveMessage2Item 格式化报告审批消息
+func FormatReportApproveMessage2Item(origin *ReportApproveMessage) (item *ReportApproveMessageItem) {
+	item = new(ReportApproveMessageItem)
+	if origin == nil {
+		return
+	}
+	item.Id = origin.Id
+	item.SendUserId = origin.SendUserId
+	item.ReceiveUserId = origin.ReceiveUserId
+	item.Content = origin.Content
+	item.Remark = origin.Remark
+	item.ReportApproveId = origin.ReportApproveId
+	item.ApproveState = origin.ApproveState
+	item.IsRead = origin.IsRead
+	item.CreateTime = utils.TimeTransferString(utils.FormatDateTime, origin.CreateTime)
+	item.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, origin.ModifyTime)
+	return
+}
+
+// ReportApproveMessageListReq 审批消息列表请求参数
+type ReportApproveMessageListReq struct {
+	PageSize     int `form:"PageSize"`
+	CurrentIndex int `form:"CurrentIndex"`
+}
+
+// ReportApproveMessageListResp 审批消息列表响应体
+type ReportApproveMessageListResp struct {
+	List        []*ReportApproveMessageItem
+	Paging      *paging.PagingItem `description:"分页数据"`
+	UnreadTotal int                `description:"消息未读数"`
+}
+
+// ReportApproveMessageReadReq 审批消息已读请求体
+type ReportApproveMessageReadReq struct {
+	MessageId int `description:"审批消息ID"`
+}

+ 215 - 0
models/report_approve/report_approve_node.go

@@ -0,0 +1,215 @@
+package report_approve
+
+import (
+	"encoding/json"
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"strings"
+	"time"
+)
+
+// ReportApproveNode 报告审批节点表
+type ReportApproveNode struct {
+	ReportApproveNodeId int       `orm:"column(report_approve_node_id);pk" description:"报告审批节点ID"`
+	ReportApproveFlowId int       `description:"报告审批流ID"`
+	PrevNodeId          int       `description:"上一个节点ID(0为开始节点)"`
+	NextNodeId          int       `description:"下一个节点ID(0为结束节点)"`
+	NodeType            int       `description:"节点类型:0-审批;1-抄送"`
+	ApproveType         int       `description:"审批类型:1-依次审批;2-会签;3-或签"`
+	Users               string    `description:"审批人信息-JSON,user_type:user-用户;role-角色,user_id:用户/角色ID"`
+	CurrVersion         int       `description:"当前版本号"`
+	CreateTime          time.Time `description:"创建时间"`
+}
+
+var ReportApproveNodeCols = struct {
+	ReportApproveNodeId string
+	ReportApproveFlowId string
+	PrevNodeId          string
+	NextNodeId          string
+	NodeType            string
+	ApproveType         string
+	Users               string
+	CurrVersion         string
+	CreateTime          string
+}{
+	ReportApproveNodeId: "report_approve_node_id",
+	ReportApproveFlowId: "report_approve_flow_id",
+	PrevNodeId:          "prev_node_id",
+	NextNodeId:          "next_node_id",
+	NodeType:            "node_type",
+	ApproveType:         "approve_type",
+	Users:               "users",
+	CurrVersion:         "curr_version",
+	CreateTime:          "create_time",
+}
+
+func (m *ReportApproveNode) TableName() string {
+	return "report_approve_node"
+}
+
+func (m *ReportApproveNode) PrimaryId() string {
+	return "report_approve_node_id"
+}
+
+func (m *ReportApproveNode) Create() (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	id, err := o.Insert(m)
+	if err != nil {
+		return
+	}
+	m.ReportApproveNodeId = int(id)
+	return
+}
+
+func (m *ReportApproveNode) CreateMulti(items []*ReportApproveNode) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("rddp")
+	_, err = o.InsertMulti(len(items), items)
+	return
+}
+
+func (m *ReportApproveNode) Update(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	_, err = o.Update(m, cols...)
+	return
+}
+
+func (m *ReportApproveNode) Del() (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	_, err = o.Raw(sql, m.ReportApproveNodeId).Exec()
+	return
+}
+
+func (m *ReportApproveNode) MultiDel(menuIds []int) (err error) {
+	if len(menuIds) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("rddp")
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s IN (%s)`, m.TableName(), m.PrimaryId(), utils.GetOrmInReplace(len(menuIds)))
+	_, err = o.Raw(sql, menuIds).Exec()
+	return
+}
+
+func (m *ReportApproveNode) GetItemById(id int) (item *ReportApproveNode, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+func (m *ReportApproveNode) GetItemByCondition(condition string, pars []interface{}, orderRule string) (item *ReportApproveNode, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	order := ``
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s %s LIMIT 1`, m.TableName(), condition, order)
+	err = o.Raw(sql, pars).QueryRow(&item)
+	return
+}
+
+func (m *ReportApproveNode) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM %s WHERE 1=1 %s`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+func (m *ReportApproveNode) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*ReportApproveNode, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC, report_approve_node_id ASC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+func (m *ReportApproveNode) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*ReportApproveNode, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s LIMIT ?,?`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+// ReportApproveNodeSaveReq 报告审批节点保存请求体
+type ReportApproveNodeSaveReq struct {
+	ApproveType int                        `description:"审批类型:1-依次审批;2-会签;3-或签"`
+	Users       []ReportApproveNodeUserReq `description:"审批人信息"`
+}
+
+// ReportApproveNodeUserReq 报告审批节点用户请求体
+type ReportApproveNodeUserReq struct {
+	UserType string `description:"审批人类型: user-用户; role-角色"`
+	UserId   int    `description:"用户/角色ID"`
+	UserName string `description:"用户/角色姓名"`
+	Sort     int    `description:"排序"`
+}
+
+// ReportApproveNodeItem 报告审批节点详情信息
+type ReportApproveNodeItem struct {
+	ReportApproveNodeId int                         `description:"报告审批节点ID"`
+	ReportApproveFlowId int                         `description:"报告审批流ID"`
+	PrevNodeId          int                         `description:"上一个节点ID(0为开始节点)"`
+	NextNodeId          int                         `description:"下一个节点ID(0为结束节点)"`
+	NodeType            int                         `description:"节点类型:0-审批;1-抄送"`
+	ApproveType         int                         `description:"审批类型:1-依次审批;2-会签;3-或签"`
+	Users               []*ReportApproveNodeUserReq `description:"审批人信息"`
+}
+
+// ReportApproveDetailNodes 审批详情-节点信息
+type ReportApproveDetailNodes struct {
+	ReportApproveNodeId int                            `description:"报告审批节点ID"`
+	ReportApproveFlowId int                            `description:"报告审批流ID"`
+	PrevNodeId          int                            `description:"上一个节点ID(0为开始节点)"`
+	NextNodeId          int                            `description:"下一个节点ID(0为结束节点)"`
+	NodeType            int                            `description:"节点类型:0-审批;1-抄送"`
+	ApproveType         int                            `description:"审批类型:1-依次审批;2-会签;3-或签"`
+	Users               []*ReportApproveDetailNodeUser `description:"审批人信息"`
+}
+
+// ReportApproveDetailNodeUser 审批详情-节点用户信息
+type ReportApproveDetailNodeUser struct {
+	ReportApproveNodeUserReq
+	ApproveRecord *ReportApproveDetailNodeUserRecord `description:"用户审批记录"`
+}
+
+// FormatReportApproveNode2Item 格式化报告审批节点信息
+func FormatReportApproveNode2Item(origin *ReportApproveNode) (item *ReportApproveNodeItem, err error) {
+	if origin == nil {
+		return
+	}
+	item = new(ReportApproveNodeItem)
+	item.ReportApproveNodeId = origin.ReportApproveNodeId
+	item.ReportApproveFlowId = origin.ReportApproveFlowId
+	item.PrevNodeId = origin.PrevNodeId
+	item.NextNodeId = origin.NextNodeId
+	item.NodeType = origin.NodeType
+	item.ApproveType = origin.ApproveType
+	item.Users = make([]*ReportApproveNodeUserReq, 0)
+	if origin.Users != "" {
+		e := json.Unmarshal([]byte(origin.Users), &item.Users)
+		if e != nil {
+			err = fmt.Errorf("node users unmarshal err: %s", e.Error())
+			return
+		}
+	}
+	return
+}

+ 220 - 0
models/report_approve/report_approve_record.go

@@ -0,0 +1,220 @@
+package report_approve
+
+import (
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"strings"
+	"time"
+)
+
+// ReportApproveRecord 报告审批记录表
+type ReportApproveRecord struct {
+	ReportApproveRecordId int       `orm:"column(report_approve_record_id);pk" description:"审批记录ID"`
+	ReportApproveId       int       `description:"审批ID"`
+	State                 int       `description:"审批状态:1-待审批;2-已审批;3-已驳回;4-已撤回"`
+	NodeId                int       `description:"节点ID"`
+	NodeType              int       `description:"节点类型:0-审批;1-抄送"`
+	PrevNodeId            int       `description:"上级节点ID"`
+	NextNodeId            int       `description:"下级节点ID"`
+	ApproveType           int       `description:"审批类型:1-依次审批;2-会签;3-或签"`
+	ApproveUserId         int       `description:"审批人ID"`
+	ApproveUserName       string    `description:"审批人姓名"`
+	ApproveUserSort       int       `description:"依次审批:用户的审批顺序"`
+	ApproveRemark         string    `description:"审批备注(驳回备注等)"`
+	ApproveTime           time.Time `description:"审批时间"`
+	CreateTime            time.Time `description:"创建时间"`
+	ModifyTime            time.Time `description:"修改时间"`
+}
+
+var ReportApproveRecordCols = struct {
+	ReportApproveRecordId string
+	ReportApproveId       string
+	State                 string
+	NodeId                string
+	NodeType              string
+	PrevNodeId            string
+	NextNodeId            string
+	ApproveType           string
+	ApproveRemark         string
+	ApproveUserId         string
+	ApproveUserName       string
+	ApproveUserSort       string
+	ApproveTime           string
+	CreateTime            string
+	ModifyTime            string
+}{
+	ReportApproveRecordId: "report_approve_record_id",
+	ReportApproveId:       "report_approve_id",
+	State:                 "state",
+	NodeId:                "node_id",
+	NodeType:              "node_type",
+	PrevNodeId:            "prev_node_id",
+	NextNodeId:            "next_node_id",
+	ApproveType:           "approve_type",
+	ApproveUserId:         "approve_user_id",
+	ApproveUserName:       "approve_user_name",
+	ApproveUserSort:       "approve_user_sort",
+	ApproveRemark:         "approve_remark",
+	ApproveTime:           "approve_time",
+	CreateTime:            "create_time",
+	ModifyTime:            "modify_time",
+}
+
+func (m *ReportApproveRecord) TableName() string {
+	return "report_approve_record"
+}
+
+func (m *ReportApproveRecord) PrimaryId() string {
+	return ReportApproveRecordCols.ReportApproveRecordId
+}
+
+func (m *ReportApproveRecord) Create() (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	id, err := o.Insert(m)
+	if err != nil {
+		return
+	}
+	m.ReportApproveRecordId = int(id)
+	return
+}
+
+func (m *ReportApproveRecord) CreateMulti(items []*ReportApproveRecord) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("rddp")
+	_, err = o.InsertMulti(len(items), items)
+	return
+}
+
+func (m *ReportApproveRecord) Update(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	_, err = o.Update(m, cols...)
+	return
+}
+
+func (m *ReportApproveRecord) Del() (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	_, err = o.Raw(sql, m.ReportApproveRecordId).Exec()
+	return
+}
+
+func (m *ReportApproveRecord) MultiDel(menuIds []int) (err error) {
+	if len(menuIds) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("rddp")
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s IN (%s)`, m.TableName(), m.PrimaryId(), utils.GetOrmInReplace(len(menuIds)))
+	_, err = o.Raw(sql, menuIds).Exec()
+	return
+}
+
+func (m *ReportApproveRecord) GetItemById(id int) (item *ReportApproveRecord, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+func (m *ReportApproveRecord) GetItemByCondition(condition string, pars []interface{}, orderRule string) (item *ReportApproveRecord, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	order := ``
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s %s LIMIT 1`, m.TableName(), condition, order)
+	err = o.Raw(sql, pars).QueryRow(&item)
+	return
+}
+
+func (m *ReportApproveRecord) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM %s WHERE 1=1 %s`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+func (m *ReportApproveRecord) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*ReportApproveRecord, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+func (m *ReportApproveRecord) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*ReportApproveRecord, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s LIMIT ?,?`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+// ReportApproveRecordItem 报告审批记录信息
+type ReportApproveRecordItem struct {
+	ReportApproveRecordId int    `description:"审批记录ID"`
+	ReportApproveId       int    `description:"审批ID"`
+	State                 int    `description:"审批状态:1-待审批;2-已审批;3-已驳回;4-已撤回"`
+	NodeId                int    `description:"节点ID"`
+	NodeType              int    `description:"节点类型:0-审批;1-抄送"`
+	PrevNodeId            int    `description:"上级节点ID"`
+	NextNodeId            int    `description:"下级节点ID"`
+	ApproveType           int    `description:"审批类型:1-依次审批;2-会签;3-或签"`
+	ApproveUserId         int    `description:"审批人ID"`
+	ApproveUserName       string `description:"审批人姓名"`
+	ApproveUserSort       int    `description:"依次审批:用户的审批顺序"`
+	ApproveRemark         string `description:"审批备注(驳回备注等)"`
+	ApproveTime           string `description:"审批时间"`
+	CreateTime            string `description:"创建时间"`
+	ModifyTime            string `description:"修改时间"`
+}
+
+// FormatReportApproveRecord2Item 格式化报告审批记录
+func FormatReportApproveRecord2Item(origin *ReportApproveRecord) (item *ReportApproveRecordItem) {
+	item = new(ReportApproveRecordItem)
+	if origin == nil {
+		return
+	}
+	item.ReportApproveRecordId = origin.ReportApproveRecordId
+	item.ReportApproveId = origin.ReportApproveId
+	item.State = origin.State
+	item.NodeId = origin.NodeId
+	item.NodeType = origin.NodeType
+	item.PrevNodeId = origin.PrevNodeId
+	item.NextNodeId = origin.NextNodeId
+	item.ApproveType = origin.ApproveType
+	item.ApproveUserId = origin.ApproveUserId
+	item.ApproveUserName = origin.ApproveUserName
+	item.ApproveUserSort = origin.ApproveUserSort
+	item.ApproveRemark = origin.ApproveRemark
+	item.ApproveTime = utils.TimeTransferString(utils.FormatDateTime, origin.ApproveTime)
+	item.CreateTime = utils.TimeTransferString(utils.FormatDateTime, origin.CreateTime)
+	item.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, origin.ModifyTime)
+	return
+}
+
+// ReportApproveDetailNodeUserRecord 审批详情-节点用户审批记录
+type ReportApproveDetailNodeUserRecord struct {
+	ReportApproveRecordId int    `description:"审批记录ID"`
+	State                 int    `description:"审批状态:1-待审批;2-已审批;3-已驳回;4-已撤回"`
+	ApproveUserId         int    `description:"审批人ID"`
+	ApproveUserName       string `description:"审批人姓名"`
+	ApproveRemark         string `description:"审批备注"`
+	ApproveTime           string `description:"审批时间"`
+}

+ 41 - 3
models/smart_report/smart_report.go

@@ -38,7 +38,7 @@ type SmartReport struct {
 	VideoSize           string    `description:"音频文件大小,单位M"`
 	AdminId             int       `description:"创建者ID"`
 	AdminRealName       string    `description:"创建者姓名"`
-	State               int       `description:"发布状态:1-待发布;2-已发布"`
+	State               int       `description:"1:未发布;2:已发布;3-待提交;4-待审批;5-已驳回;6-已通过"`
 	LastModifyAdminId   int       `description:"最后更新人ID"`
 	LastModifyAdminName string    `description:"最后更新人姓名"`
 	ContentModifyTime   time.Time `description:"内容更新时间"`
@@ -56,6 +56,8 @@ type SmartReport struct {
 	HeadImg             string    `description:"报告头图地址"`
 	EndImg              string    `description:"报告尾图地址"`
 	CanvasColor         string    `description:"画布颜色"`
+	ApproveTime         time.Time `description:"审批时间"`
+	ApproveId           int       `description:"审批ID"`
 }
 
 func (m *SmartReport) TableName() string {
@@ -154,7 +156,8 @@ func (m *SmartReport) GetPageItemsByCondition(condition string, pars []interface
 	if len(fieldArr) == 0 {
 		fields = `*`
 	}
-	order := `ORDER BY FIELD(state,3,1,2,4), create_time DESC`
+	// 排序:1:未发布;2:已发布;3-待提交;4-待审批;5-已驳回;6-已通过
+	order := `ORDER BY FIELD(state,3,1,4,5,6,2), modify_time DESC`
 	if orderRule != "" {
 		order = ` ORDER BY ` + orderRule
 	}
@@ -198,7 +201,7 @@ type SmartReportItem struct {
 	ContentModifyTime   string  `description:"内容更新时间"`
 	Pv                  int     `description:"pv"`
 	Uv                  int     `description:"uv"`
-	State               int     `description:"发布状态:1-待发布;2-已发布"`
+	State               int     `description:"1:未发布;2:已发布;3-待提交;4-待审批;5-已驳回;6-已通过"`
 	PublishTime         string  `description:"发布时间"`
 	PrePublishTime      string  `description:"预发布时间"`
 	MsgIsSend           int     `description:"消息是否已发送:0-否;1-是"`
@@ -207,6 +210,7 @@ type SmartReportItem struct {
 	DetailPdfUrl        string  `description:"报告详情PDF地址"`
 	CreateTime          string  `description:"创建时间"`
 	ModifyTime          string  `description:"修改时间"`
+	ApproveTime         string  `description:"审批时间"`
 	CanEdit             bool    `description:"是否可编辑"`
 	Editor              string  `description:"当前编辑人"`
 	HeadImg             string  `description:"报告头图地址"`
@@ -258,6 +262,7 @@ func FormatSmartReport2Item(origin *SmartReport) (item *SmartReportItem) {
 	item.HeadImg = origin.HeadImg
 	item.EndImg = origin.EndImg
 	item.CanvasColor = origin.CanvasColor
+	item.ApproveTime = utils.TimeTransferString(utils.FormatDateTime, origin.ApproveTime)
 	return
 }
 
@@ -361,3 +366,36 @@ type Report2ImgQueueReq struct {
 	ReportType int    `description:"报告类型: 1-研报; 2-智能研报"`
 	ReportCode string `description:"报告唯一编码"`
 }
+
+// UpdateSmartReportsStateByCond 批量更新报告状态
+func UpdateSmartReportsStateByCond(classifyFirstId, classifySecondId, oldState, newState int) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	cond := ``
+	if classifyFirstId > 0 {
+		cond += fmt.Sprintf(` AND classify_id_first = %d`, classifyFirstId)
+	}
+	if classifySecondId > 0 {
+		cond += fmt.Sprintf(` AND classify_id_second = %d`, classifySecondId)
+	}
+	sql := fmt.Sprintf(`UPDATE smart_report SET state = ?, pre_publish_time = NULL WHERE state = ? %s`, cond)
+	_, err = o.Raw(sql, newState, oldState).Exec()
+	return
+}
+
+// UpdateSmartReportsStateBySecondIds 批量更新二级分类报告状态
+func UpdateSmartReportsStateBySecondIds(oldState, newState int, secondIds []int) (err error) {
+	if len(secondIds) <= 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("rddp")
+	// (有审批流的)未发布->待提交
+	sql := fmt.Sprintf(`UPDATE smart_report SET state = ?, pre_publish_time = NULL WHERE state = ? AND classify_id_second IN (%s)`, utils.GetOrmInReplace(len(secondIds)))
+	_, err = o.Raw(sql, newState, oldState, secondIds).Exec()
+	if err != nil {
+		return
+	}
+	// (无审批流的)待提交->未发布
+	sql = fmt.Sprintf(`UPDATE smart_report SET state = ?, pre_publish_time = NULL WHERE state = ? AND classify_id_second NOT IN (%s)`, utils.GetOrmInReplace(len(secondIds)))
+	_, err = o.Raw(sql, oldState, newState, secondIds).Exec()
+	return
+}

+ 180 - 0
routers/commentsRouter.go

@@ -4498,6 +4498,24 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/english_report:EnglishReportController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/english_report:EnglishReportController"],
+        beego.ControllerComments{
+            Method: "CancelApprove",
+            Router: `/approve/cancel`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/english_report:EnglishReportController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/english_report:EnglishReportController"],
+        beego.ControllerComments{
+            Method: "SubmitApprove",
+            Router: `/approve/submit`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/english_report:EnglishReportController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/english_report:EnglishReportController"],
         beego.ControllerComments{
             Method: "Author",
@@ -4894,6 +4912,132 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/report_approve:ReportApproveController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/report_approve:ReportApproveController"],
+        beego.ControllerComments{
+            Method: "Approve",
+            Router: `/approve`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/report_approve:ReportApproveController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/report_approve:ReportApproveController"],
+        beego.ControllerComments{
+            Method: "Cancel",
+            Router: `/cancel`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/report_approve:ReportApproveController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/report_approve:ReportApproveController"],
+        beego.ControllerComments{
+            Method: "CheckApproveOpen",
+            Router: `/classify/check_open`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/report_approve:ReportApproveController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/report_approve:ReportApproveController"],
+        beego.ControllerComments{
+            Method: "Detail",
+            Router: `/detail`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/report_approve:ReportApproveController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/report_approve:ReportApproveController"],
+        beego.ControllerComments{
+            Method: "List",
+            Router: `/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/report_approve:ReportApproveController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/report_approve:ReportApproveController"],
+        beego.ControllerComments{
+            Method: "MessageList",
+            Router: `/message/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/report_approve:ReportApproveController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/report_approve:ReportApproveController"],
+        beego.ControllerComments{
+            Method: "MessageRead",
+            Router: `/message/read`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/report_approve:ReportApproveController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/report_approve:ReportApproveController"],
+        beego.ControllerComments{
+            Method: "Refuse",
+            Router: `/refuse`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/report_approve:ReportApproveFlowController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/report_approve:ReportApproveFlowController"],
+        beego.ControllerComments{
+            Method: "Add",
+            Router: `/flow/add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/report_approve:ReportApproveFlowController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/report_approve:ReportApproveFlowController"],
+        beego.ControllerComments{
+            Method: "Detail",
+            Router: `/flow/detail`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/report_approve:ReportApproveFlowController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/report_approve:ReportApproveFlowController"],
+        beego.ControllerComments{
+            Method: "Edit",
+            Router: `/flow/edit`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/report_approve:ReportApproveFlowController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/report_approve:ReportApproveFlowController"],
+        beego.ControllerComments{
+            Method: "List",
+            Router: `/flow/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/report_approve:ReportApproveFlowController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/report_approve:ReportApproveFlowController"],
+        beego.ControllerComments{
+            Method: "Remove",
+            Router: `/flow/remove`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/report_approve:ReportApproveFlowController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/report_approve:ReportApproveFlowController"],
+        beego.ControllerComments{
+            Method: "ReportClassifyTree",
+            Router: `/report/classify_tree`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/roadshow:CalendarController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/roadshow:CalendarController"],
         beego.ControllerComments{
             Method: "ResearcherList",
@@ -5344,6 +5488,24 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/smart_report:SmartReportController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/smart_report:SmartReportController"],
+        beego.ControllerComments{
+            Method: "CancelApprove",
+            Router: `/approve/cancel`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/smart_report:SmartReportController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/smart_report:SmartReportController"],
+        beego.ControllerComments{
+            Method: "SubmitApprove",
+            Router: `/approve/submit`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/smart_report:SmartReportController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/smart_report:SmartReportController"],
         beego.ControllerComments{
             Method: "Detail",
@@ -6541,6 +6703,24 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers:ReportController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers:ReportController"],
+        beego.ControllerComments{
+            Method: "CancelApprove",
+            Router: `/approve/cancel`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers:ReportController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers:ReportController"],
+        beego.ControllerComments{
+            Method: "SubmitApprove",
+            Router: `/approve/submit`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers:ReportController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers:ReportController"],
         beego.ControllerComments{
             Method: "Author",

+ 7 - 0
routers/router.go

@@ -21,6 +21,7 @@ import (
 	"eta/eta_api/controllers/data_stat"
 	"eta/eta_api/controllers/english_report"
 	"eta/eta_api/controllers/eta_trial"
+	"eta/eta_api/controllers/report_approve"
 	"eta/eta_api/controllers/roadshow"
 	"eta/eta_api/controllers/sandbox"
 	"eta/eta_api/controllers/semantic_analysis"
@@ -328,6 +329,12 @@ func init() {
 				&cross_variety.TagController{},
 			),
 		),
+		web.NSNamespace("/report_approve",
+			web.NSInclude(
+				&report_approve.ReportApproveController{},
+				&report_approve.ReportApproveFlowController{},
+			),
+		),
 	)
 	web.AddNamespace(ns)
 }

+ 1 - 1
services/english_report.go

@@ -509,7 +509,7 @@ func UpdateAllPublishedEnglishReportToEs() (err error) {
 	// 获取所有已发布的报告
 	var condition string
 	var pars []interface{}
-	condition = ` AND state = 2 `
+	condition = ` AND state IN (2, 6) `
 	reportList, err := models.GetEnglishReportByCondition(condition, pars)
 	count := 0
 	failCount := 0

+ 1 - 1
services/report.go

@@ -564,7 +564,7 @@ func UpdatePublishedReportToEs() (err error) {
 	// 获取所有已发布的报告
 	var condition string
 	var pars []interface{}
-	condition = ` AND state = 2 `
+	condition = ` AND state IN (2, 6) `
 	reportList, err := models.GetReportList(condition, pars, "ficc", 1, 5000)
 	count := 0
 	failCount := 0

+ 1013 - 0
services/report_approve.go

@@ -0,0 +1,1013 @@
+package services
+
+import (
+	"eta/eta_api/models"
+	"eta/eta_api/models/report_approve"
+	"eta/eta_api/models/smart_report"
+	"eta/eta_api/services/alarm_msg"
+	"eta/eta_api/utils"
+	"fmt"
+	"sort"
+	"time"
+)
+
+// GetReportClassifyTreeRecursive 递归获取报告分类树
+func GetReportClassifyTreeRecursive(list []*models.Classify, parentId int) []*report_approve.ReportClassifyTreeItem {
+	res := make([]*report_approve.ReportClassifyTreeItem, 0)
+	for _, v := range list {
+		if v.ParentId == parentId {
+			t := new(report_approve.ReportClassifyTreeItem)
+			t.ClassifyId = v.Id
+			t.ClassifyName = v.ClassifyName
+			t.ParentId = v.ParentId
+			t.Children = GetReportClassifyTreeRecursive(list, v.Id)
+			res = append(res, t)
+		}
+	}
+	return res
+}
+
+// CheckReportApproveFlowChange 校验是否可变更分类
+func CheckReportApproveFlowChange(reportType, classifyFirstId, classifySecondId int) (ok bool, err error) {
+	var count int
+	cond := ` AND classify_id_first = ? AND classify_id_second = ? AND state = ?`
+	pars := make([]interface{}, 0)
+	pars = append(pars, classifyFirstId, classifySecondId, models.ReportStateWaitApprove)
+	switch reportType {
+	case report_approve.FlowReportTypeChinese:
+		ct, e := models.GetReportListCount(cond, pars, "")
+		if e != nil {
+			err = fmt.Errorf("GetReportListCount err: %s", e.Error())
+			return
+		}
+		count = ct
+	case report_approve.FlowReportTypeEnglish:
+		ct, e := models.GetEnglishReportListCount(cond, pars, "")
+		if e != nil {
+			err = fmt.Errorf("GetEnglishReportListCount err: %s", e.Error())
+			return
+		}
+		count = ct
+	case report_approve.FlowReportTypeSmart:
+		ob := new(smart_report.SmartReport)
+		ct, e := ob.GetCountByCondition(cond, pars)
+		if e != nil {
+			err = fmt.Errorf("SmartReport GetCountByCondition err: %s", e.Error())
+			return
+		}
+		count = ct
+	default:
+		err = fmt.Errorf("报告类型有误")
+		return
+	}
+	if count <= 0 {
+		ok = true
+	}
+	return
+}
+
+// CheckReportOpenApprove 校验报告是否开启了审批流
+func CheckReportOpenApprove(reportType, firstId, secondId int) (opening bool, err error) {
+	// 获取审批配置
+	confMap, e := models.GetBusinessConf()
+	if e != nil {
+		err = fmt.Errorf("GetBusinessConf err: %s", e.Error())
+		return
+	}
+	openMap := map[string]bool{"false": false, "true": true}
+	openApprove := openMap[confMap[models.BusinessConfIsReportApprove]]
+
+	// 查询对应分类是否有审批流
+	flowOb := new(report_approve.ReportApproveFlow)
+	flowCond := fmt.Sprintf(` AND %s = ? AND %s = ? AND %s = ?`, report_approve.ReportApproveFlowCols.ReportType, report_approve.ReportApproveFlowCols.ClassifyFirstId, report_approve.ReportApproveFlowCols.ClassifySecondId)
+	flowPars := make([]interface{}, 0)
+	flowPars = append(flowPars, reportType, firstId, secondId)
+	flowItem, e := flowOb.GetItemByCondition(flowCond, flowPars, "")
+	if e != nil && e.Error() != utils.ErrNoRow() {
+		err = fmt.Errorf("ApproveFlow GetItemByCondition err: %s", e.Error())
+		return
+	}
+
+	// 开启审批/有审批流
+	if openApprove && (flowItem != nil || confMap[models.BusinessConfReportApproveType] == models.BusinessConfReportApproveTypeOther) {
+		opening = true
+		return
+	}
+	return
+}
+
+// CheckReportCurrState 校验报告当前应有的状态
+func CheckReportCurrState(reportType, firstId, secondId, operate int) (state int, err error) {
+	// 获取审批配置
+	confMap, e := models.GetBusinessConf()
+	if e != nil {
+		err = fmt.Errorf("GetBusinessConf err: %s", e.Error())
+		return
+	}
+	openMap := map[string]bool{"false": false, "true": true}
+	openApprove := openMap[confMap[models.BusinessConfIsReportApprove]]
+
+	// 查询对应分类是否有审批流
+	flowOb := new(report_approve.ReportApproveFlow)
+	flowCond := fmt.Sprintf(` AND %s = ? AND %s = ? AND %s = ?`, report_approve.ReportApproveFlowCols.ReportType, report_approve.ReportApproveFlowCols.ClassifyFirstId, report_approve.ReportApproveFlowCols.ClassifySecondId)
+	flowPars := make([]interface{}, 0)
+	flowPars = append(flowPars, reportType, firstId, secondId)
+	flowItem, e := flowOb.GetItemByCondition(flowCond, flowPars, "")
+	if e != nil && e.Error() != utils.ErrNoRow() {
+		err = fmt.Errorf("ApproveFlow GetItemByCondition err: %s", e.Error())
+		return
+	}
+
+	// 开启审批/有审批流
+	if openApprove && (flowItem != nil || confMap[models.BusinessConfReportApproveType] == models.BusinessConfReportApproveTypeOther) {
+		// 操作为无审批的操作时, 会转为有审批的初始状态-待提交
+		stateMap := map[int]int{
+			models.ReportOperateAdd:           models.ReportStateWaitSubmit,  // 新增
+			models.ReportOperateEdit:          models.ReportStateWaitSubmit,  // 编辑
+			models.ReportOperatePublish:       models.ReportStateWaitSubmit,  // 发布
+			models.ReportOperateCancelPublish: models.ReportStateWaitSubmit,  // 取消发布
+			models.ReportOperateSubmitApprove: models.ReportStateWaitApprove, // 提审
+			models.ReportOperateCancelApprove: models.ReportStateWaitSubmit,  // 撤回
+		}
+		state = stateMap[operate]
+		return
+	}
+
+	// 关闭审批/分类无审批
+	// 操作为有审批的操作时, 会转为无审批的初始状态-未发布
+	stateMap := map[int]int{
+		models.ReportOperateAdd:           models.ReportStateUnpublished, // 新增
+		models.ReportOperateEdit:          models.ReportStateUnpublished, // 编辑
+		models.ReportOperatePublish:       models.ReportStatePublished,   // 发布
+		models.ReportOperateCancelPublish: models.ReportStateUnpublished, // 取消发布
+		models.ReportOperateSubmitApprove: models.ReportStateUnpublished, // 提审
+		models.ReportOperateCancelApprove: models.ReportStateUnpublished, // 撤回
+	}
+	state = stateMap[operate]
+	return
+}
+
+// SubmitReportApprove 提交审批
+func SubmitReportApprove(reportType, reportId int, reportTitle string, firstId, secondId int, sysAdminId int, sysAdminName string) (approveId int, err error) {
+	// 默认内部审批, 如果是走的第三方审批, 那么仅修改状态
+	confMap, e := models.GetBusinessConf()
+	if e != nil {
+		err = fmt.Errorf("GetBusinessConf err: %s", e.Error())
+		return
+	}
+	openMap := map[string]bool{"false": false, "true": true}
+	openApprove := openMap[confMap[models.BusinessConfIsReportApprove]]
+	if !openApprove {
+		err = fmt.Errorf("未开启审批")
+		return
+	}
+	confApproveType := confMap[models.BusinessConfReportApproveType]
+	if confApproveType == "" {
+		confApproveType = models.BusinessConfReportApproveTypeEta
+	}
+	if confApproveType == models.BusinessConfReportApproveTypeOther {
+		return
+	}
+
+	// 查询审批流
+	flowOb := new(report_approve.ReportApproveFlow)
+	flowCond := fmt.Sprintf(` AND %s = ? AND %s = ? AND %s = ?`, report_approve.ReportApproveFlowCols.ReportType, report_approve.ReportApproveFlowCols.ClassifyFirstId, report_approve.ReportApproveFlowCols.ClassifySecondId)
+	flowPars := make([]interface{}, 0)
+	flowPars = append(flowPars, reportType, firstId, secondId)
+	flowItem, e := flowOb.GetItemByCondition(flowCond, flowPars, "")
+	if e != nil {
+		err = fmt.Errorf("ApproveFlow GetItemByCondition err: %s", e.Error())
+		return
+	}
+
+	// 查询审批节点
+	nodeOb := new(report_approve.ReportApproveNode)
+	nodeCond := fmt.Sprintf(` AND %s = ? AND %s = ?`, report_approve.ReportApproveNodeCols.ReportApproveFlowId, report_approve.ReportApproveNodeCols.CurrVersion)
+	nodePars := make([]interface{}, 0)
+	nodePars = append(nodePars, flowItem.ReportApproveFlowId, flowItem.CurrVersion)
+	nodeItems, e := nodeOb.GetItemsByCondition(nodeCond, nodePars, []string{}, "")
+	if e != nil {
+		err = fmt.Errorf("ApproveNodes GetItemsByCondition err: %s", e.Error())
+		return
+	}
+	if len(nodeItems) == 0 {
+		err = fmt.Errorf("无审批节点")
+		return
+	}
+
+	// 取出首个节点
+	firstNodeItem := new(report_approve.ReportApproveNode)
+	for _, v := range nodeItems {
+		if v.PrevNodeId == 0 {
+			firstNodeItem = v
+			continue
+		}
+	}
+	if firstNodeItem == nil {
+		err = fmt.Errorf("首个审批节点有误")
+		return
+	}
+
+	// 审批信息
+	now := time.Now().Local()
+	newApprove := new(report_approve.ReportApprove)
+	newApprove.ReportType = reportType
+	newApprove.ReportId = reportId
+	newApprove.ReportTitle = reportTitle
+	newApprove.ClassifyFirstId = firstId
+	newApprove.ClassifySecondId = secondId
+	newApprove.State = report_approve.ReportApproveStateApproving
+	newApprove.FlowId = flowItem.ReportApproveFlowId
+	newApprove.FlowVersion = flowItem.CurrVersion
+	newApprove.StartNodeId = firstNodeItem.ReportApproveNodeId
+	newApprove.CurrNodeId = firstNodeItem.ReportApproveNodeId
+	newApprove.ApplyUserId = sysAdminId
+	newApprove.ApplyUserName = sysAdminName
+	newApprove.CreateTime = now
+	newApprove.ModifyTime = now
+	if e = newApprove.Create(); e != nil {
+		err = fmt.Errorf("生成审批信息失败, Err: %s", e.Error())
+		return
+	}
+	approveId = newApprove.ReportApproveId
+
+	// 生成节点审批记录
+	err = BuildNextNodeRecordAndMsg(firstNodeItem, newApprove.ReportApproveId, sysAdminId, sysAdminName)
+	return
+}
+
+// CancelReportApprove 撤回审批
+func CancelReportApprove(reportType, reportId, approveId, sysAdminId int, sysAdminName string) (err error) {
+	// 默认内部审批, 如果是走的第三方审批, 那么仅修改状态
+	confMap, e := models.GetBusinessConf()
+	if e != nil {
+		err = fmt.Errorf("GetBusinessConf err: %s", e.Error())
+		return
+	}
+	openMap := map[string]bool{"false": false, "true": true}
+	openApprove := openMap[confMap[models.BusinessConfIsReportApprove]]
+	if !openApprove {
+		//err = fmt.Errorf("未开启审批")
+		return
+	}
+	confApproveType := confMap[models.BusinessConfReportApproveType]
+	if confApproveType == "" {
+		confApproveType = models.BusinessConfReportApproveTypeEta
+	}
+	// 第三方审批仅修改报告状态
+	if confApproveType == models.BusinessConfReportApproveTypeOther {
+		e = updateReportApproveState(reportType, reportId, 0, models.ReportStateWaitSubmit)
+		if e != nil {
+			err = fmt.Errorf("更新报告审批撤回失败, Err: %s", e.Error())
+			return
+		}
+		return
+	}
+
+	// 修改审批信息状态
+	approveOb := new(report_approve.ReportApprove)
+	approveItem, e := approveOb.GetItemById(approveId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			return
+		}
+		err = fmt.Errorf("approve GetItemById err: %s", e.Error())
+		return
+	}
+	if approveItem.State == report_approve.ReportApproveStateCancel {
+		return
+	}
+	approveItem.State = report_approve.ReportApproveStateCancel
+	cols := []string{"State", "ModifyTime"}
+	if e = approveItem.Update(cols); e != nil {
+		err = fmt.Errorf("approve Update err: %s", e.Error())
+		return
+	}
+
+	// 修改报告状态
+	e = updateReportApproveState(reportType, reportId, 0, models.ReportStateWaitSubmit)
+	if e != nil {
+		err = fmt.Errorf("更新报告审批撤回失败, Err: %s", e.Error())
+		return
+	}
+
+	// 推送撤回消息
+	go func() {
+		recordOb := new(report_approve.ReportApproveRecord)
+		recordCond := fmt.Sprintf(` AND %s = ?`, report_approve.ReportApproveRecordCols.ReportApproveId)
+		recordPars := make([]interface{}, 0)
+		recordPars = append(recordPars, approveId)
+		recordItems, e := recordOb.GetItemsByCondition(recordCond, recordPars, []string{}, "")
+		if e != nil {
+			utils.FileLog.Info(fmt.Sprintf("approve record GetItemsByCondition err: %s", e.Error()))
+			return
+		}
+
+		messageOb := new(report_approve.ReportApproveMessage)
+		messages := make([]*report_approve.ReportApproveMessage, 0)
+		for _, v := range recordItems {
+			m := new(report_approve.ReportApproveMessage)
+			m.SendUserId = sysAdminId
+			m.ReceiveUserId = v.ApproveUserId
+			m.Content = fmt.Sprintf("%s提交的【研报审批】已撤回", sysAdminName)
+			m.ReportApproveId = approveId
+			m.ApproveState = report_approve.ReportApproveStateCancel
+			m.CreateTime = time.Now().Local()
+			m.ModifyTime = time.Now().Local()
+			messages = append(messages, m)
+		}
+		e = messageOb.CreateMulti(messages)
+		if e != nil {
+			utils.FileLog.Info(fmt.Sprintf("CancelReportApprove messages err: %s", e.Error()))
+			return
+		}
+	}()
+	return
+}
+
+// updateReportApproveState 更新报告审批状态
+func updateReportApproveState(reportType, reportId, approveId, state int) (err error) {
+	updateCols := []string{"ApproveId", "State", "ModifyTime"}
+	if reportType == report_approve.FlowReportTypeChinese {
+		reportOb := new(models.Report)
+		reportItem, e := reportOb.GetItemById(reportId)
+		// 报告可能会被删除, 这里查不到就忽略掉
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			err = fmt.Errorf("获取中文研报失败, Err: %s", e.Error())
+			return
+		}
+		if reportItem != nil {
+			reportItem.ApproveId = approveId
+			reportItem.State = state
+			reportItem.ModifyTime = time.Now().Local()
+			if state == models.ReportStatePass || state == models.ReportStateRefused {
+				updateCols = append(updateCols, "ApproveTime")
+				reportItem.ApproveTime = time.Now().Local()
+			}
+			if state == models.ReportStatePass {
+				updateCols = append(updateCols, "PublishTime")
+				reportItem.PublishTime = time.Now().Local()
+			}
+			if e = reportItem.UpdateReport(updateCols); e != nil {
+				err = fmt.Errorf("更新中文研报审批状态失败, Err: %s", e.Error())
+				return
+			}
+		}
+	}
+	if reportType == report_approve.FlowReportTypeEnglish {
+		reportItem, e := models.GetEnglishReportItemById(reportId)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			err = fmt.Errorf("获取英文研报失败, Err: %s", e.Error())
+			return
+		}
+		if reportItem != nil {
+			reportItem.ApproveId = approveId
+			reportItem.State = state
+			reportItem.ModifyTime = time.Now().Local()
+			if state == models.ReportStatePass || state == models.ReportStateRefused {
+				updateCols = append(updateCols, "ApproveTime")
+				reportItem.ApproveTime = time.Now().Local()
+			}
+			if state == models.ReportStatePass {
+				updateCols = append(updateCols, "PublishTime")
+				reportItem.PublishTime = time.Now().Local()
+			}
+			if e = reportItem.UpdateReport(updateCols); e != nil {
+				err = fmt.Errorf("更新英文研报审批状态失败, Err: %s", e.Error())
+				return
+			}
+		}
+	}
+	if reportType == report_approve.FlowReportTypeSmart {
+		reportOb := new(smart_report.SmartReport)
+		reportItem, e := reportOb.GetItemById(reportId)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			err = fmt.Errorf("获取智能研报失败, Err: %s", e.Error())
+			return
+		}
+		if reportItem != nil {
+			reportItem.ApproveId = approveId
+			reportItem.State = state
+			reportItem.ModifyTime = time.Now().Local()
+			if state == models.ReportStatePass || state == models.ReportStateRefused {
+				updateCols = append(updateCols, "ApproveTime")
+				reportItem.ApproveTime = time.Now().Local()
+			}
+			if state == models.ReportStatePass {
+				updateCols = append(updateCols, "PublishTime")
+				reportItem.PublishTime = time.Now().Local()
+			}
+			if e = reportItem.Update(updateCols); e != nil {
+				err = fmt.Errorf("更新智能研报审批状态失败, Err: %s", e.Error())
+				return
+			}
+		}
+	}
+	return
+}
+
+// PassReportApprove 通过审批
+func PassReportApprove(approveItem *report_approve.ReportApprove, recordItem *report_approve.ReportApproveRecord, sysAdminId int) (tips string, err error) {
+	if approveItem == nil {
+		err = fmt.Errorf("审批信息有误")
+		return
+	}
+	if recordItem == nil {
+		err = fmt.Errorf("审批记录有误")
+		return
+	}
+
+	// 查询审批流和审批流节点
+	flowOb := new(report_approve.ReportApproveFlow)
+	flowItem, e := flowOb.GetItemById(approveItem.FlowId)
+	if e != nil {
+		err = fmt.Errorf("获取审批流失败, Err: %s", e.Error())
+		return
+	}
+	nodeOb := new(report_approve.ReportApproveNode)
+	nodeCond := fmt.Sprintf(` AND %s = ? AND %s = ?`, report_approve.ReportApproveNodeCols.ReportApproveFlowId, report_approve.ReportApproveNodeCols.CurrVersion)
+	nodePars := make([]interface{}, 0)
+	nodePars = append(nodePars, flowItem.ReportApproveFlowId, flowItem.CurrVersion)
+	nodeItems, e := nodeOb.GetItemsByCondition(nodeCond, nodePars, []string{}, "")
+	if e != nil {
+		err = fmt.Errorf("ApproveNodes GetItemsByCondition err: %s", e.Error())
+		return
+	}
+	if len(nodeItems) == 0 {
+		err = fmt.Errorf("无审批节点")
+		return
+	}
+	nodeMap := make(map[int]*report_approve.ReportApproveNode)
+	for _, v := range nodeItems {
+		nodeMap[v.ReportApproveNodeId] = v
+	}
+
+	// 取出审批记录的节点
+	currNodeItem := nodeMap[recordItem.NodeId]
+	if currNodeItem == nil {
+		err = fmt.Errorf("当前节点信息有误")
+		return
+	}
+	currNode, e := report_approve.FormatReportApproveNode2Item(currNodeItem)
+	if e != nil {
+		err = fmt.Errorf("当前节点信息有误, Err: %s", e.Error())
+		return
+	}
+	now := time.Now().Local()
+	recordItem.State = report_approve.ReportApproveStatePass
+	recordItem.ApproveTime = now
+	recordItem.ModifyTime = now
+	recordCols := []string{"State", "ApproveTime", "ModifyTime"}
+	lastApprove := false
+
+	// 依次审批
+	if currNode.ApproveType == report_approve.NodeApproveTypeRoll {
+		if e = recordItem.Update(recordCols); e != nil {
+			err = fmt.Errorf("更新审批记录状态失败, Err: %s", e.Error())
+			return
+		}
+
+		// 检查依次审批情况
+		sort.Slice(currNode.Users, func(k, j int) bool {
+			return currNode.Users[k].Sort < currNode.Users[j].Sort
+		})
+		userLen := len(currNode.Users)
+		//lastRoll := false
+		nextUser := new(report_approve.ReportApproveNodeUserReq) // 下一个审批人, 为nil则表示当前审批人即为最后
+		for k, v := range currNode.Users {
+			// 当前审批人
+			if v.UserId == sysAdminId && recordItem.ApproveUserSort == v.Sort {
+				//if k == (userLen - 1) {
+				//	lastRoll = true
+				//	break
+				//}
+				if (k + 1) < userLen {
+					nextUser = currNode.Users[k+1]
+				}
+			}
+		}
+		//if lastRoll && currNode.NextNodeId == 0 {
+		//	lastApprove = true
+		//}
+
+		// 当前节点下一个审批人, 生成下一个审批记录且return
+		if nextUser.UserId > 0 {
+			newRecord := new(report_approve.ReportApproveRecord)
+			newRecord.ReportApproveId = recordItem.ReportApproveId
+			newRecord.State = report_approve.ReportApproveStateApproving
+			newRecord.NodeId = currNode.ReportApproveNodeId
+			newRecord.PrevNodeId = currNode.PrevNodeId
+			newRecord.NextNodeId = currNode.NextNodeId
+			newRecord.ApproveType = currNode.ApproveType
+			newRecord.ApproveUserId = nextUser.UserId
+			newRecord.ApproveUserName = nextUser.UserName
+			newRecord.ApproveUserSort = nextUser.Sort
+			newRecord.CreateTime = now
+			newRecord.ModifyTime = now
+			if e = newRecord.Create(); e != nil {
+				err = fmt.Errorf("生成审批记录失败, Err: %s", e.Error())
+				return
+			}
+
+			// 推送审批消息
+			go func() {
+				messageItem := new(report_approve.ReportApproveMessage)
+				messageItem.SendUserId = approveItem.ApplyUserId
+				messageItem.ReceiveUserId = nextUser.UserId
+				messageItem.Content = "您有新的待办任务"
+				messageItem.Remark = fmt.Sprintf("%s提交的【研报审批】需要您审批,请及时处理", approveItem.ApplyUserName)
+				messageItem.ReportApproveId = approveItem.ReportApproveId
+				messageItem.ApproveState = report_approve.ReportApproveStateApproving
+				messageItem.CreateTime = now
+				messageItem.ModifyTime = now
+				if e = messageItem.Create(); e != nil {
+					utils.FileLog.Info(fmt.Sprintf("PassReportApprove message err: %s", e.Error()))
+					return
+				}
+			}()
+			return
+		}
+
+		// 更新审批当前节点并进入下一个节点
+		if currNode.NextNodeId > 0 {
+			nextNode := nodeMap[currNode.NextNodeId]
+			approveItem.CurrNodeId = currNode.NextNodeId
+			approveItem.ModifyTime = now
+			if e = approveItem.Update([]string{"CurrNodeId", "ModifyTime"}); e != nil {
+				err = fmt.Errorf("更新审批当前节点失败, Err: %s", e.Error())
+				return
+			}
+			err = BuildNextNodeRecordAndMsg(nextNode, approveItem.ReportApproveId, approveItem.ApplyUserId, approveItem.ApplyUserName)
+			return
+		} else {
+			// 最后一个节点
+			lastApprove = true
+		}
+	}
+
+	// 会签
+	if currNode.ApproveType == report_approve.NodeApproveTypeAll {
+		// 查询其他审批人是否已审批
+		otherOb := new(report_approve.ReportApproveRecord)
+		otherCond := fmt.Sprintf(` AND %s = ? AND %s = ? AND %s <> ?`, report_approve.ReportApproveRecordCols.ReportApproveId, report_approve.ReportApproveRecordCols.NodeId, report_approve.ReportApproveRecordCols.ApproveUserId)
+		otherPars := make([]interface{}, 0)
+		otherPars = append(otherPars, approveItem.ReportApproveId, recordItem.NodeId, sysAdminId)
+		otherRecords, e := otherOb.GetItemsByCondition(otherCond, otherPars, []string{}, "")
+		if e != nil {
+			err = fmt.Errorf("获取节点审批记录失败, Err: %s", e.Error())
+			return
+		}
+		otherPass := true
+		for _, v := range otherRecords {
+			if v.State != report_approve.ReportApproveStatePass {
+				otherPass = false
+			}
+		}
+
+		// 其他人未审批, 仅更新当前审批记录
+		if e = recordItem.Update(recordCols); e != nil {
+			err = fmt.Errorf("更新审批记录状态失败, Err: %s", e.Error())
+			return
+		}
+
+		// 其他人已审批且为最后节点
+		if otherPass && currNode.NextNodeId == 0 {
+			lastApprove = true
+		}
+
+		// 其他人已审批且不为最后节点, 进入下一节点
+		if otherPass && currNode.NextNodeId > 0 {
+			nextNode := nodeMap[currNode.NextNodeId]
+			approveItem.CurrNodeId = currNode.NextNodeId
+			approveItem.ModifyTime = now
+			if e = approveItem.Update([]string{"CurrNodeId", "ModifyTime"}); e != nil {
+				err = fmt.Errorf("更新审批当前节点失败, Err: %s", e.Error())
+				return
+			}
+			err = BuildNextNodeRecordAndMsg(nextNode, approveItem.ReportApproveId, approveItem.ApplyUserId, approveItem.ApplyUserName)
+			return
+		}
+	}
+
+	// 或签
+	if currNode.ApproveType == report_approve.NodeApproveTypeAny {
+		// 需检查一下审批的当前节点和记录的节点是否匹配, 不匹配可能是因为另外的审批人已通过, 所以此处应给提示
+		// 前端也有做相应的判断,但是两个人同时进入审批详情页时就可能出现这种情况
+		if approveItem.CurrNodeId != recordItem.NodeId {
+			tips = "该节点已完成审批, 请刷新页面"
+			return
+		}
+
+		if e = recordItem.Update(recordCols); e != nil {
+			err = fmt.Errorf("更新审批记录状态失败, Err: %s", e.Error())
+			return
+		}
+		if currNode.NextNodeId == 0 {
+			lastApprove = true
+		}
+		if currNode.NextNodeId > 0 {
+			nextNode := nodeMap[currNode.NextNodeId]
+			approveItem.CurrNodeId = currNode.NextNodeId
+			approveItem.ModifyTime = now
+			if e = approveItem.Update([]string{"CurrNodeId", "ModifyTime"}); e != nil {
+				err = fmt.Errorf("更新审批当前节点失败, Err: %s", e.Error())
+				return
+			}
+			err = BuildNextNodeRecordAndMsg(nextNode, approveItem.ReportApproveId, approveItem.ApplyUserId, approveItem.ApplyUserName)
+			return
+		}
+	}
+
+	// 最后一个审批, 更新审批记录、审批、报告状态、推送消息给申请人
+	if lastApprove {
+		if e = recordItem.Update(recordCols); e != nil {
+			err = fmt.Errorf("更新审批记录状态失败, Err: %s", e.Error())
+			return
+		}
+		approveItem.State = report_approve.ReportApproveStatePass
+		approveItem.ApproveTime = now
+		approveItem.ModifyTime = now
+		approveCols := []string{"State", "ApproveTime", "ModifyTime"}
+		if e = approveItem.Update(approveCols); e != nil {
+			err = fmt.Errorf("更新审批信息失败, Err: %s", e.Error())
+			return
+		}
+		if e = updateReportApproveState(approveItem.ReportType, approveItem.ReportId, approveItem.ReportApproveId, models.ReportStatePass); e != nil {
+			err = fmt.Errorf("更新报告审批状态失败, Err: %s", e.Error())
+			return
+		}
+
+		go func() {
+			messageItem := new(report_approve.ReportApproveMessage)
+			messageItem.SendUserId = sysAdminId
+			messageItem.ReceiveUserId = approveItem.ApplyUserId
+			messageItem.Content = "您提交的审批已通过"
+			messageItem.Remark = "您提交的【研报审批】已通过"
+			messageItem.ReportApproveId = approveItem.ReportApproveId
+			messageItem.ApproveState = report_approve.ReportApproveStatePass
+			messageItem.CreateTime = now
+			messageItem.ModifyTime = now
+			if e = messageItem.Create(); e != nil {
+				utils.FileLog.Info(fmt.Sprintf("PassReportApprove message err: %s", e.Error()))
+				return
+			}
+		}()
+
+		// 审批通过之后的处理
+		go func() {
+			if e = AfterReportApprovePass(approveItem.ReportType, approveItem.ReportId); e != nil {
+				utils.FileLog.Info(fmt.Sprintf("AfterReportApprovePass err: %s, ReportType: %d, ReportId: %d", e.Error(), approveItem.ReportType, approveItem.ReportId))
+				return
+			}
+		}()
+	}
+	return
+}
+
+// RefuseReportApprove 驳回审批
+func RefuseReportApprove(approveItem *report_approve.ReportApprove, recordItem *report_approve.ReportApproveRecord, approveRemark string, sysAdminId int) (err error) {
+	if approveItem == nil {
+		err = fmt.Errorf("审批信息有误")
+		return
+	}
+	if recordItem == nil {
+		err = fmt.Errorf("审批记录有误")
+		return
+	}
+
+	// 更新审批记录
+	now := time.Now().Local()
+	recordItem.State = report_approve.ReportApproveStateRefuse
+	recordItem.ApproveRemark = approveRemark
+	recordItem.ApproveTime = now
+	recordItem.ModifyTime = now
+	recordCols := []string{"State", "ApproveRemark", "ApproveTime", "ModifyTime"}
+	if e := recordItem.Update(recordCols); e != nil {
+		err = fmt.Errorf("更新审批记录状态失败, Err: %s", e.Error())
+		return
+	}
+
+	// 驳回-更新审批, 报告状态, 推送消息
+	approveItem.State = report_approve.ReportApproveStateRefuse
+	approveItem.ApproveRemark = approveRemark
+	approveItem.ApproveTime = now
+	approveItem.ModifyTime = now
+	approveCols := []string{"State", "ApproveRemark", "ApproveTime", "ModifyTime"}
+	if e := approveItem.Update(approveCols); e != nil {
+		err = fmt.Errorf("更新审批状态失败, Err: %s", e.Error())
+		return
+	}
+	if e := updateReportApproveState(approveItem.ReportType, approveItem.ReportId, approveItem.ReportApproveId, models.ReportStateRefused); e != nil {
+		err = fmt.Errorf("更新报告状态失败, Err: %s", e.Error())
+		return
+	}
+
+	// 推送驳回消息给申请人
+	go func() {
+		messageItem := new(report_approve.ReportApproveMessage)
+		messageItem.SendUserId = sysAdminId
+		messageItem.ReceiveUserId = approveItem.ApplyUserId
+		messageItem.Content = "您提交的审批被驳回"
+		messageItem.Remark = "您提交的【研报审批】已被驳回"
+		messageItem.ReportApproveId = approveItem.ReportApproveId
+		messageItem.ApproveState = report_approve.ReportApproveStateRefuse
+		messageItem.CreateTime = now
+		messageItem.ModifyTime = now
+		if e := messageItem.Create(); e != nil {
+			utils.FileLog.Info(fmt.Sprintf("ApproveReport message err: %s", e.Error()))
+			return
+		}
+	}()
+	return
+}
+
+// BuildNextNodeRecordAndMsg 生成下一个节点的审批记录并推送消息
+func BuildNextNodeRecordAndMsg(approveNodeItem *report_approve.ReportApproveNode, approveId, sysAdminId int, sysAdminName string) (err error) {
+	if approveNodeItem == nil {
+		err = fmt.Errorf("approve node nil")
+		return
+	}
+
+	// 根据节点审批方式生成审批记录
+	now := time.Now().Local()
+	approveNode, e := report_approve.FormatReportApproveNode2Item(approveNodeItem)
+	if e != nil {
+		err = fmt.Errorf("FormatReportApproveNode2Item err: %s", e.Error())
+		return
+	}
+	if len(approveNode.Users) == 0 {
+		err = fmt.Errorf("审批节点用户有误")
+		return
+	}
+	newRecords := make([]*report_approve.ReportApproveRecord, 0)
+	sort.Slice(approveNode.Users, func(k, j int) bool {
+		return approveNode.Users[k].Sort < approveNode.Users[j].Sort
+	})
+	for _, u := range approveNode.Users {
+		r := new(report_approve.ReportApproveRecord)
+		r.ReportApproveId = approveId
+		r.State = report_approve.ReportApproveStateApproving
+		r.NodeId = approveNode.ReportApproveNodeId
+		r.PrevNodeId = approveNode.PrevNodeId
+		r.NextNodeId = approveNode.NextNodeId
+		r.ApproveType = approveNode.ApproveType
+		r.ApproveUserId = u.UserId
+		r.ApproveUserName = u.UserName
+		r.ApproveUserSort = u.Sort
+		r.CreateTime = now
+		r.ModifyTime = now
+		newRecords = append(newRecords, r)
+		// 依次审批仅生成一条记录
+		if approveNode.ApproveType == report_approve.NodeApproveTypeRoll {
+			break
+		}
+	}
+
+	recordOb := new(report_approve.ReportApproveRecord)
+	if e = recordOb.CreateMulti(newRecords); e != nil {
+		err = fmt.Errorf("生成节点审批记录失败, Err: %s", e.Error())
+		return
+	}
+
+	// 推送审批消息
+	go func() {
+		messageOb := new(report_approve.ReportApproveMessage)
+		messages := make([]*report_approve.ReportApproveMessage, 0)
+		for _, v := range newRecords {
+			m := new(report_approve.ReportApproveMessage)
+			m.SendUserId = sysAdminId
+			m.ReceiveUserId = v.ApproveUserId
+			m.Content = "您有新的待办任务"
+			m.Remark = fmt.Sprintf("%s提交的【研报审批】需要您审批,请及时处理", sysAdminName)
+			m.ReportApproveId = approveId
+			m.ApproveState = report_approve.ReportApproveStateApproving
+			m.CreateTime = now
+			m.ModifyTime = now
+			messages = append(messages, m)
+		}
+		e = messageOb.CreateMulti(messages)
+		if e != nil {
+			utils.FileLog.Info(fmt.Sprintf("BuildNextNodeRecordAndMsg messages err: %s", e.Error()))
+			return
+		}
+	}()
+	return
+}
+
+// AfterReportApprovePass 报告审批通过后的处理
+func AfterReportApprovePass(reportType, reportId int) (err error) {
+	// 中文研报
+	if reportType == report_approve.FlowReportTypeChinese {
+		report, e := models.GetReportById(reportId)
+		if e != nil {
+			if e.Error() == utils.ErrNoRow() {
+				return
+			}
+			err = fmt.Errorf("获取研报信息失败, Err: %s", e.Error())
+			return
+		}
+		_ = CreateVideo(report)
+		_ = UpdateReportEs(report.Id, models.ReportStatePublished)
+		return
+	}
+
+	// 英文研报
+	if reportType == report_approve.FlowReportTypeEnglish {
+		_ = UpdateEnglishReportEs(reportId, models.ReportStatePublished)
+		return
+	}
+
+	// 智能研报
+	if reportType == report_approve.FlowReportTypeSmart {
+		ob := new(smart_report.SmartReport)
+		item, e := ob.GetItemById(reportId)
+		if e != nil {
+			if e.Error() == utils.ErrNoRow() {
+				return
+			}
+			err = fmt.Errorf("获取智能研报信息失败, Err: %s", e.Error())
+			return
+		}
+
+		// 写入队列
+		var queue smart_report.Report2ImgQueueReq
+		queue.ReportType = 2
+		queue.ReportCode = item.ReportCode
+		_ = utils.Rc.LPush(utils.CACHE_CREATE_REPORT_IMGPDF_QUEUE, queue)
+
+		// 生成音频
+		if item.VideoUrl == "" {
+			SmartReportBuildVideoAndUpdate(item)
+		}
+
+		// ES更新报告
+		_ = SmartReportElasticUpsert(item.SmartReportId, models.ReportStatePublished)
+	}
+	return
+}
+
+// CheckCloseReportApproveConf 校验是否可以关闭报告审批
+func CheckCloseReportApproveConf() (yes bool, err error) {
+	// 查询待审批中的报告数量
+	count, e := models.GetReportStateCount(models.ReportStateWaitApprove)
+	if e != nil {
+		err = fmt.Errorf("查询审批中的报告数量失败, Err: %s", e.Error())
+		return
+	}
+	if count > 0 {
+		return
+	}
+	count, e = models.GetEnglishReportStateCount(models.ReportStateWaitApprove)
+	if e != nil {
+		err = fmt.Errorf("查询审批中的英文报告数量失败, Err: %s", e.Error())
+		return
+	}
+	if count > 0 {
+		return
+	}
+	smartOb := new(smart_report.SmartReport)
+	cond := ` AND state = ?`
+	pars := make([]interface{}, 0)
+	pars = append(pars, models.ReportStateWaitApprove)
+	count, e = smartOb.GetCountByCondition(cond, pars)
+	if e != nil {
+		err = fmt.Errorf("查询审批中的智能报告数量失败, Err: %s", e.Error())
+		return
+	}
+	if count > 0 {
+		return
+	}
+	yes = true
+	return
+}
+
+// FlowOperateResetReportState 审批流变化-重置报告的初始状态
+func FlowOperateResetReportState(reportType, classifyFirstId, classifySecondId, oldState, State int) (err error) {
+	defer func() {
+		if err != nil {
+			tips := fmt.Sprintf("审批流变化-重置报告初始状态失败, ErrMsg: %s", err.Error())
+			utils.FileLog.Info(tips)
+			go alarm_msg.SendAlarmMsg(tips, 2)
+		}
+	}()
+
+	// 中文研报
+	if reportType == report_approve.FlowReportTypeChinese {
+		e := models.UpdateReportsStateByCond(classifyFirstId, classifySecondId, oldState, State)
+		if e != nil {
+			err = fmt.Errorf("UpdateReportsStateByCond err: %s", e.Error())
+		}
+		return
+	}
+
+	// 英文研报
+	if reportType == report_approve.FlowReportTypeEnglish {
+		e := models.UpdateEnglishReportsStateByCond(classifyFirstId, classifySecondId, oldState, State)
+		if e != nil {
+			err = fmt.Errorf("UpdateEnglishReportsStateByCond err: %s", e.Error())
+		}
+		return
+	}
+
+	// 智能研报
+	if reportType == report_approve.FlowReportTypeSmart {
+		e := smart_report.UpdateSmartReportsStateByCond(classifyFirstId, classifySecondId, oldState, State)
+		if e != nil {
+			err = fmt.Errorf("UpdateSmartReportsStateByCond err: %s", e.Error())
+		}
+		return
+	}
+	return
+}
+
+// ConfigChangeResetReportState 审批配置变化-重置报告的初始状态
+func ConfigChangeResetReportState(changeType string) (err error) {
+	defer func() {
+		if err != nil {
+			tips := fmt.Sprintf("审批配置变化-重置报告初始状态失败, ErrMsg: %s", err.Error())
+			utils.FileLog.Info(tips)
+			go alarm_msg.SendAlarmMsg(tips, 2)
+		}
+	}()
+
+	// 关闭审批-待提交->未发布
+	if changeType == "" {
+		e := models.UpdateReportsStateByCond(0, 0, models.ReportStateWaitSubmit, models.ReportStateUnpublished)
+		if e != nil {
+			err = fmt.Errorf("UpdateReportsStateByCond err: %s", e.Error())
+		}
+		e = models.UpdateEnglishReportsStateByCond(0, 0, models.ReportStateWaitSubmit, models.ReportStateUnpublished)
+		if e != nil {
+			err = fmt.Errorf("UpdateEnglishReportsStateByCond err: %s", e.Error())
+		}
+		e = smart_report.UpdateSmartReportsStateByCond(0, 0, models.ReportStateWaitSubmit, models.ReportStateUnpublished)
+		if e != nil {
+			err = fmt.Errorf("UpdateSmartReportsStateByCond err: %s", e.Error())
+		}
+		return
+	}
+
+	// 开启内部审批-未发布->部分待提交
+	if changeType == models.BusinessConfReportApproveTypeEta {
+		flowOb := new(report_approve.ReportApproveFlow)
+		flows, e := flowOb.GetItemsByCondition(``, make([]interface{}, 0), []string{}, "")
+		if e != nil {
+			err = fmt.Errorf("GetApproveFlows err: %s", e.Error())
+			return
+		}
+		reportSecondIds, englishSecondIds, smartSecondIds := make([]int, 0), make([]int, 0), make([]int, 0)
+		for _, v := range flows {
+			if v.ReportType == report_approve.FlowReportTypeChinese {
+				reportSecondIds = append(reportSecondIds, v.ClassifySecondId)
+				continue
+			}
+			if v.ReportType == report_approve.FlowReportTypeEnglish {
+				englishSecondIds = append(englishSecondIds, v.ClassifySecondId)
+				continue
+			}
+			if v.ReportType == report_approve.FlowReportTypeSmart {
+				smartSecondIds = append(smartSecondIds, v.ClassifySecondId)
+			}
+		}
+		if len(reportSecondIds) > 0 {
+			e = models.UpdateReportsStateBySecondIds(models.ReportStateUnpublished, models.ReportStateWaitSubmit, reportSecondIds)
+			if e != nil {
+				err = fmt.Errorf("UpdateReportsStateBySecondIds err: %s", e.Error())
+				return
+			}
+		}
+		if len(englishSecondIds) > 0 {
+			e = models.UpdateEnglishReportsStateBySecondIds(models.ReportStateUnpublished, models.ReportStateWaitSubmit, englishSecondIds)
+			if e != nil {
+				err = fmt.Errorf("UpdateEnglishReportsStateBySecondIds err: %s", e.Error())
+				return
+			}
+		}
+		if len(smartSecondIds) > 0 {
+			e = smart_report.UpdateSmartReportsStateBySecondIds(models.ReportStateUnpublished, models.ReportStateWaitSubmit, smartSecondIds)
+			if e != nil {
+				err = fmt.Errorf("UpdateSmartReportsStateBySecondIds err: %s", e.Error())
+				return
+			}
+		}
+		return
+	}
+
+	// 开启第三方审批->未发布->待提交
+	if changeType == models.BusinessConfReportApproveTypeOther {
+		e := models.UpdateReportsStateByCond(0, 0, models.ReportStateUnpublished, models.ReportStateWaitSubmit)
+		if e != nil {
+			err = fmt.Errorf("UpdateReportsStateByCond err: %s", e.Error())
+		}
+		e = models.UpdateEnglishReportsStateByCond(0, 0, models.ReportStateUnpublished, models.ReportStateWaitSubmit)
+		if e != nil {
+			err = fmt.Errorf("UpdateEnglishReportsStateByCond err: %s", e.Error())
+		}
+		e = smart_report.UpdateSmartReportsStateByCond(0, 0, models.ReportStateUnpublished, models.ReportStateWaitSubmit)
+		if e != nil {
+			err = fmt.Errorf("UpdateSmartReportsStateByCond err: %s", e.Error())
+		}
+		return
+	}
+	return
+}

+ 3 - 4
services/smart_report/smart_report.go → services/smart_report.go

@@ -1,10 +1,9 @@
-package smart_report
+package services
 
 import (
 	"encoding/json"
 	"eta/eta_api/models"
 	"eta/eta_api/models/smart_report"
-	"eta/eta_api/services"
 	"eta/eta_api/services/alarm_msg"
 	"eta/eta_api/utils"
 	"fmt"
@@ -26,7 +25,7 @@ func SmartReportBuildVideoAndUpdate(item *smart_report.SmartReport) {
 		}
 	}()
 
-	videoUrl, videoName, videoSize, videoPlaySeconds, e := services.CreateReportVideo(item.Title, item.Content, time.Now().Local().Format(utils.FormatDateTime))
+	videoUrl, videoName, videoSize, videoPlaySeconds, e := CreateReportVideo(item.Title, item.Content, time.Now().Local().Format(utils.FormatDateTime))
 	if e != nil {
 		err = fmt.Errorf("create audio err: %s", e.Error())
 		return
@@ -124,7 +123,7 @@ func SmartReportElasticUpsert(smartReportId int, state int) (err error) {
 	esReport.ClassifyNameSecond = item.ClassifyNameSecond
 	esReport.StageStr = strconv.Itoa(item.Stage)
 	esReport.Frequency = item.Frequency
-	if err = services.EsAddOrEditSmartReport(utils.SmartReportIndexName, strconv.Itoa(item.SmartReportId), esReport); err != nil {
+	if err = EsAddOrEditSmartReport(utils.SmartReportIndexName, strconv.Itoa(item.SmartReportId), esReport); err != nil {
 		return
 	}
 	return