浏览代码

Merge branch 'feature/eta_1.3.8' into debug

hsun 1 年之前
父节点
当前提交
000613e6d2

+ 6 - 5
controllers/base_auth.go

@@ -208,11 +208,12 @@ func (c *BaseAuthController) Prepare() {
 			for _, s := range apis {
 				apiMap[s] = true
 			}
-			if !apiMap[uri] {
-				c.JSON(models.BaseResponse{Ret: 403, Msg: "无权访问!", ErrMsg: "无权访问!"}, false, false)
-				c.StopRun()
-				return
-			}
+			// TODO: 关闭调试
+			//if !apiMap[uri] {
+			//	c.JSON(models.BaseResponse{Ret: 403, Msg: "无权访问!", ErrMsg: "无权访问!"}, false, false)
+			//	c.StopRun()
+			//	return
+			//}
 		} else {
 			c.JSON(models.BaseResponse{Ret: 408, Msg: "请求异常,请联系客服!", ErrMsg: "POST之外的请求,暂不支持"}, false, false)
 			c.StopRun()

+ 3 - 3
controllers/english_report/report.go

@@ -307,7 +307,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 +369,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 +491,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 {

+ 9 - 2
controllers/report.go

@@ -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,9 @@ func (this *ReportController) PublishReport() {
 		br.ErrMsg = "参数错误,报告id不可为空"
 		return
 	}
+
+	// TODO:根据审批开关及对应分类审批流判断当前报告初始状态
+
 	reportArr := strings.Split(reportIds, ",")
 	tips := ""
 	for _, v := range reportArr {
@@ -400,6 +403,8 @@ func (this *ReportController) PublishCancleReport() {
 		go services.UpdateReportEs(req.ReportIds, 1)
 	}
 
+	// TODO:根据审批开关及对应分类审批流判断当前报告初始状态
+
 	// 获取审批流设置
 	confKey := "approval_flow"
 	confTmp, e := company.GetConfigDetailByCode(confKey)
@@ -514,6 +519,8 @@ func (this *ReportController) Add() {
 		return
 	}
 
+	// TODO:根据审批开关及对应分类审批流判断当前报告初始状态
+
 	item := new(models.Report)
 	item.AddType = req.AddType
 	item.ClassifyIdFirst = req.ClassifyIdFirst

+ 1 - 0
controllers/report_approve/report_approve.go

@@ -0,0 +1 @@
+package report_approve

+ 710 - 0
controllers/report_approve/report_approve_flow.go

@@ -0,0 +1,710 @@
+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 /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.GetAllEnglishClassify()
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取英文分类失败, Err: " + e.Error()
+		return
+	}
+	for _, v := range enClassify {
+		enClassifyIdName[v.Id] = v.ClassifyName
+	}
+
+	for _, v := range list {
+		t := report_approve.FormatReportApproveFlow2Item(v)
+		if v.ReportType == report_approve.FlowReportTypeEnglish {
+			t.ReportClassify = fmt.Sprintf("%s/%s/%s", report_approve.FlowReportTypeMap[v.ReportType], 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.ReportApproveDetailItem
+// @router /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
+	}
+	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.ReportApproveDetailItem
+// @router /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
+		}
+	}
+
+	// 变更了报告分类时, 判断是否允许变更
+	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.ReportApproveDetailItem
+// @router /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 /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
+	}
+
+	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 = "获取成功"
+}

+ 3 - 3
controllers/smart_report/smart_report.go

@@ -859,7 +859,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 +906,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 +982,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 {

+ 13 - 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 商户配置表

+ 14 - 2
models/db.go

@@ -10,6 +10,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"
@@ -161,6 +162,9 @@ func init() {
 	// 初始化跨品种分析表
 	initCrossVariety()
 
+	// 报告审批
+	initReportApprove()
+
 	// 初始化部分数据表变量(直接init会有顺序问题=_=!)
 	data_manage.InitEdbSourceVar()
 }
@@ -471,8 +475,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), // 智能研报-资源表
 	)
 }
@@ -487,3 +491,11 @@ func initCrossVariety() {
 		new(cross_variety.ChartInfoCrossVariety), // 跨品种分析配置表
 	)
 }
+
+// initReportApprove 报告审批相关表
+func initReportApprove() {
+	orm.RegisterModel(
+		new(report_approve.ReportApproveFlow), // 审批流表
+		new(report_approve.ReportApproveNode), // 审批节点表
+	)
+}

+ 4 - 2
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,7 @@ type EnglishReport struct {
 	FromReportId       int       `description:"继承的报告ID(英文策略报告ID)"`
 	AdminId            int       `description:"创建者账号"`
 	AdminRealName      string    `description:"创建者姓名"`
+	ApproveTime        time.Time `description:"审批时间"`
 }
 
 func GetEnglishReportStage(classifyIdFirst, classifyIdSecond int) (count int, err error) {
@@ -240,7 +241,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 +265,7 @@ type EnglishReportList struct {
 	FullClassifyName   string    `description:"顶级分类名/父级分类名/当前分类名"`
 	ClassifyIdRoot     int       `description:"顶级分类id"`
 	ClassifyNameRoot   string    `description:"顶级分类名称"`
+	ApproveTime        string    `description:"审批时间"`
 }
 
 type EnglishReportListResp struct {

+ 15 - 3
models/report.go

@@ -8,6 +8,16 @@ import (
 	"time"
 )
 
+// 报告状态
+const (
+	ReportStateUnpublished = 1 // 未发布
+	ReportStatePublished   = 2 // 已发布
+	ReportStateWaitSubmit  = 3 // 待提交
+	ReportStateWaitApprove = 4 // 审批中
+	ReportStateRefused     = 5 // 已驳回
+	ReportStatePass        = 6 // 已通过
+)
+
 type Report struct {
 	Id                 int       `orm:"column(id)" description:"报告Id"`
 	AddType            int       `description:"新增方式:1:新增报告,2:继承报告"`
@@ -21,7 +31,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 +50,7 @@ type Report struct {
 	MsgSendTime        time.Time `description:"模版消息发送时间"`
 	AdminId            int       `description:"创建者账号"`
 	AdminRealName      string    `description:"创建者姓名"`
+	ApproveTime        time.Time `description:"审批时间"`
 }
 
 type ReportList struct {
@@ -55,7 +66,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 +91,7 @@ type ReportList struct {
 	Editor             string                    `description:"编辑人"`
 	AdminId            int                       `description:"创建者账号"`
 	AdminRealName      string                    `description:"创建者姓名"`
+	ApproveTime        string                    `description:"审批时间"`
 }
 
 type ReportListResp struct {
@@ -1050,4 +1062,4 @@ 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
-}
+}

+ 399 - 0
models/report_approve/report_approve_flow.go

@@ -0,0 +1,399 @@
+package report_approve
+
+import (
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"strings"
+	"time"
+)
+
+const (
+	FlowReportTypeChinese = 1
+	FlowReportTypeEnglish = 2
+	FlowReportTypeSmart   = 3
+)
+
+var FlowReportTypeMap = map[int]string{
+	FlowReportTypeChinese: "中文研报",
+	FlowReportTypeEnglish: "英文研报",
+	FlowReportTypeSmart:   "智能研报",
+}
+
+// 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
+}
+
+// ReportApproveDetailItem 报告审批流详情信息
+type ReportApproveDetailItem struct {
+	ReportApproveFlowItem `description:"审批流信息"`
+	Nodes                 []*ReportApproveNodeItem `description:"节点信息"`
+}
+
+// FormatFlowAndNodesItem2Detail 格式化审批流详情
+func FormatFlowAndNodesItem2Detail(flowItem *ReportApproveFlow, nodeItems []*ReportApproveNode) (detail *ReportApproveDetailItem, err error) {
+	if flowItem == nil {
+		return
+	}
+	detail = new(ReportApproveDetailItem)
+	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:"分页数据"`
+}

+ 209 - 0
models/report_approve/report_approve_node.go

@@ -0,0 +1,209 @@
+package report_approve
+
+import (
+	"encoding/json"
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"strings"
+	"time"
+)
+
+const (
+	NodeApproveTypeRoll = 1
+	NodeApproveTypeAll  = 2
+	NodeApproveTypeAny  = 3
+)
+
+const (
+	NodeUserTypeNormal = "user"
+	NodeUserTypeRole   = "role"
+)
+
+// 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:"审批人信息"`
+}
+
+// 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
+}

+ 1 - 0
models/report_approve/report_approve_record.go

@@ -0,0 +1 @@
+package report_approve

+ 5 - 2
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,7 @@ type SmartReport struct {
 	HeadImg             string    `description:"报告头图地址"`
 	EndImg              string    `description:"报告尾图地址"`
 	CanvasColor         string    `description:"画布颜色"`
+	ApproveTime         time.Time `description:"审批时间"`
 }
 
 func (m *SmartReport) TableName() string {
@@ -198,7 +199,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 +208,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 +260,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
 }
 

+ 54 - 0
routers/commentsRouter.go

@@ -4777,6 +4777,60 @@ func init() {
             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: `/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: `/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: `/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: `/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: `/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",

+ 6 - 0
routers/router.go

@@ -20,6 +20,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,11 @@ func init() {
 				&cross_variety.TagController{},
 			),
 		),
+		web.NSNamespace("/report_approve",
+			web.NSInclude(
+				&report_approve.ReportApproveFlowController{},
+			),
+		),
 	)
 	web.AddNamespace(ns)
 }

+ 63 - 0
services/report_approve.go

@@ -0,0 +1,63 @@
+package services
+
+import (
+	"eta/eta_api/models"
+	"eta/eta_api/models/report_approve"
+	"eta/eta_api/models/smart_report"
+	"fmt"
+)
+
+// 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
+}