Explorar o código

Merge branch 'master' of http://8.136.199.33:3000/eta_gn_server/eta_api into bzq/knowledge_Bi

zqbao hai 1 día
pai
achega
e84544d642
Modificáronse 58 ficheiros con 5244 adicións e 1085 borrados
  1. 124 103
      controllers/base_auth.go
  2. 368 178
      controllers/classify.go
  3. 42 276
      controllers/data_manage/chart_info.go
  4. 1 1
      controllers/data_manage/cross_variety/variety.go
  5. 1 1
      controllers/data_manage/edb_info_calculate.go
  6. 3 2
      controllers/data_manage/excel/balance_table.go
  7. 26 9
      controllers/data_manage/excel/excel_info.go
  8. 100 5
      controllers/data_manage/manual.go
  9. 0 67
      controllers/data_manage/predict_edb_info.go
  10. 19 0
      controllers/data_manage/range_analysis/chart_info.go
  11. 1 1
      controllers/data_manage/supply_analysis/variety.go
  12. 300 0
      controllers/image_conf_controller.go
  13. 647 0
      controllers/ppt_report.go
  14. 317 140
      controllers/ppt_v2.go
  15. 300 0
      controllers/ppt_v2_history.go
  16. 22 1
      controllers/report_chapter.go
  17. 443 0
      controllers/report_history.go
  18. 251 23
      controllers/report_v2.go
  19. 35 20
      controllers/sandbox/sandbox.go
  20. 1 0
      models/business_conf.go
  21. 91 14
      models/classify.go
  22. 87 0
      models/classify_visible.go
  23. 4 3
      models/data_manage/chart_info.go
  24. 24 4
      models/data_manage/edb_info.go
  25. 1 1
      models/data_manage/edb_info_relation.go
  26. 3 0
      models/data_manage/excel/request/mixed_table.go
  27. 9 0
      models/data_manage/predict_edb_conf.go
  28. 1 1
      models/data_manage/predict_edb_conf_calculate_mapping.go
  29. 69 0
      models/image_conf.go
  30. 261 41
      models/ppt_v2.go
  31. 89 0
      models/ppt_v2_history.go
  32. 220 57
      models/report.go
  33. 1 0
      models/report_chapter.go
  34. 86 0
      models/report_history.go
  35. 213 0
      models/report_message.go
  36. 7 0
      models/report_v2.go
  37. 9 0
      models/system/response/sys_role_admin.go
  38. 7 0
      models/system/sys_admin.go
  39. 171 0
      routers/commentsRouter.go
  40. 15 0
      routers/router.go
  41. 145 17
      services/classify.go
  42. 17 0
      services/data/chart_info_elastic.go
  43. 10 0
      services/data/edb_info.go
  44. 5 0
      services/data/excel/excel_info.go
  45. 34 0
      services/data/excel/mixed_table.go
  46. 13 34
      services/data/predict_edb_info.go
  47. 1 0
      services/data/range_analysis/chart_info.go
  48. 36 6
      services/excel/lucky_sheet.go
  49. 46 43
      services/excel/lucky_sheet_table.go
  50. 2 2
      services/ppt.go
  51. 180 0
      services/ppt_report.go
  52. 2 2
      services/report.go
  53. 115 0
      services/report_outer.go
  54. 173 5
      services/report_v2.go
  55. 3 1
      services/smart_report.go
  56. 66 0
      services/task.go
  57. 14 5
      utils/common.go
  58. 13 22
      utils/constants.go

+ 124 - 103
controllers/base_auth.go

@@ -494,29 +494,13 @@ func (c *BaseAuthController) Prepare() {
 		c.Lang = lang
 	}
 
-	isOk, token, resp := checkToken(c)
+	isOk, session, resp := checkToken(c)
 	if !isOk {
 		_ = c.JSON(resp, false, false)
 		c.StopRun()
 		return
 	}
 
-	//accountStr := authorizationArr[1]
-	//accountArr := strings.Split(accountStr, "=")
-	//account := accountArr[1]
-
-	session, err := system.GetSysSessionByToken(token)
-	//fmt.Println("session:", session)
-	if err != nil {
-		if utils.IsErrNoRow(err) {
-			c.JSON(models.BaseResponse{Ret: 408, Msg: "信息已变更,请重新登陆!", ErrMsg: "Token 信息已变更:Token: " + token}, false, false)
-			c.StopRun()
-			return
-		}
-		c.JSON(models.BaseResponse{Ret: 408, Msg: "网络异常,请稍后重试!", ErrMsg: "获取用户信息异常,Eerr:" + err.Error()}, false, false)
-		c.StopRun()
-		return
-	}
 	if session == nil {
 		c.JSON(models.BaseResponse{Ret: 408, Msg: "网络异常,请稍后重试!", ErrMsg: "sesson is empty "}, false, false)
 		c.StopRun()
@@ -524,17 +508,7 @@ func (c *BaseAuthController) Prepare() {
 	}
 	//校验token是否合法
 	// JWT校验Token和Account
-	account := utils.MD5(session.UserName)
-	if !utils.CheckToken(account, token) {
-		c.JSON(models.BaseResponse{Ret: 408, Msg: "鉴权失败,请重新登录!", ErrMsg: "登录失效,请重新登陆!,CheckToken Fail"}, false, false)
-		c.StopRun()
-		return
-	}
-	if time.Now().After(session.ExpiredTime) {
-		c.JSON(models.BaseResponse{Ret: 408, Msg: "请重新登录!", ErrMsg: "获取用户信息异常,Eerr:" + err.Error()}, false, false)
-		c.StopRun()
-		return
-	}
+
 	admin, err := system.GetSysUserById(session.SysUserId)
 	if err != nil {
 		if utils.IsErrNoRow(err) {
@@ -558,36 +532,36 @@ func (c *BaseAuthController) Prepare() {
 		return
 	}
 
-	// 如果当前登录态是不可信设备的,那么需要做过期校验
-	if session.IsRemember != 1 {
-		loginKey := fmt.Sprint(utils.CACHE_ACCESS_TOKEN_LOGIN, session.Id)
-		loginInfo, _ := utils.Rc.RedisString(loginKey)
-		if loginInfo == `` {
-			c.JSON(models.BaseResponse{Ret: 408, Msg: "超时未操作,系统自动退出!", ErrMsg: "超时未操作,系统自动退出!"}, false, false)
-			c.StopRun()
-			return
-		}
-
-		//if loginInfo != "1" {
-		//	lastLoginTime := admin.LastLoginTime
-		//
-		//	lastLoginTimeObj, err := time.Parse(utils.FormatDateWallWithLoc, lastLoginTime)
-		//	fmt.Println(lastLoginTimeObj, err)
-		//
-		//	msg := `该账号于` + lastLoginTimeObj.Format(utils.FormatDateTime) + "在其他网络登录。此客户端已退出登录。"
-		//	c.JSON(models.BaseResponse{Ret: 408, Msg: msg, ErrMsg: msg}, false, false)
-		//	c.StopRun()
-		//	return
-		//}
-
-		// 如果是ETA体验版-更新活跃时长/更新登录时长的接口请求, 则不更新Token时长
-		if uri != `/adminapi/eta_trial/user/login_duration` && uri != `/adminapi/eta_trial/user/active` {
-			utils.Rc.Put(loginKey, "1", utils.LoginCacheTime*time.Minute)
-			// 不信任名单也同步更新
-			noTrustLoginKey := fmt.Sprint(utils.CACHE_ACCESS_TOKEN_LOGIN_NO_TRUST, admin.AdminId)
-			utils.Rc.Put(noTrustLoginKey, session.Id, utils.LoginCacheTime*time.Minute)
-		}
-	}
+	//// 如果当前登录态是不可信设备的,那么需要做过期校验
+	//if session.IsRemember != 1 {
+	//	loginKey := fmt.Sprint(utils.CACHE_ACCESS_TOKEN_LOGIN, session.Id)
+	//	loginInfo, _ := utils.Rc.RedisString(loginKey)
+	//	if loginInfo == `` {
+	//		c.JSON(models.BaseResponse{Ret: 408, Msg: "超时未操作,系统自动退出!", ErrMsg: "超时未操作,系统自动退出!"}, false, false)
+	//		c.StopRun()
+	//		return
+	//	}
+	//
+	//	//if loginInfo != "1" {
+	//	//	lastLoginTime := admin.LastLoginTime
+	//	//
+	//	//	lastLoginTimeObj, err := time.Parse(utils.FormatDateWallWithLoc, lastLoginTime)
+	//	//	fmt.Println(lastLoginTimeObj, err)
+	//	//
+	//	//	msg := `该账号于` + lastLoginTimeObj.Format(utils.FormatDateTime) + "在其他网络登录。此客户端已退出登录。"
+	//	//	c.JSON(models.BaseResponse{Ret: 408, Msg: msg, ErrMsg: msg}, false, false)
+	//	//	c.StopRun()
+	//	//	return
+	//	//}
+	//
+	//	// 如果是ETA体验版-更新活跃时长/更新登录时长的接口请求, 则不更新Token时长
+	//	if uri != `/adminapi/eta_trial/user/login_duration` && uri != `/adminapi/eta_trial/user/active` {
+	//		utils.Rc.Put(loginKey, "1", utils.LoginCacheTime*time.Minute)
+	//		// 不信任名单也同步更新
+	//		noTrustLoginKey := fmt.Sprint(utils.CACHE_ACCESS_TOKEN_LOGIN_NO_TRUST, admin.AdminId)
+	//		utils.Rc.Put(noTrustLoginKey, session.Id, utils.LoginCacheTime*time.Minute)
+	//	}
+	//}
 
 	admin.RoleTypeCode = GetSysUserRoleTypeCode(admin.RoleTypeCode)
 	c.SysUser = admin
@@ -641,12 +615,56 @@ func (c *BaseAuthController) Prepare() {
 // @datetime 2024-10-30 11:29:37
 // @param c *BaseAuthController
 // @return isOk bool
-// @return token string
+// @return session system.SysSession
 // @return resp models.BaseResponse
-func checkToken(c *BaseAuthController) (isOk bool, token string, resp models.BaseResponse) {
+func checkToken(c *BaseAuthController) (isOk bool, session *system.SysSession, resp models.BaseResponse) {
 	// 是否校验成功
 	isOk = true
 	uri := c.Ctx.Input.URI()
+
+	var token string
+
+	// 单点登录逻辑
+	aiUser := c.Ctx.GetCookie("ai_user")
+	if aiUser == `` {
+		aiUser = c.Ctx.GetCookie("ai_token")
+	}
+	//fmt.Println("ai_user:", aiUser)
+
+	// 如果cookie里面没有这个,那么就过期重新登录
+	if aiUser == `` {
+		resp = models.BaseResponse{Ret: 408, Msg: "请重新授权!", ErrMsg: "ai_user or ai_token empty"}
+		isOk = false
+		return
+	}
+
+	// cookie过期逻辑
+	{
+		aiUserExpireStr := c.Ctx.GetCookie("expire")
+		if aiUserExpireStr == `` {
+			resp = models.BaseResponse{Ret: 408, Msg: "请重新授权!", ErrMsg: "expire empty"}
+			isOk = false
+			return
+		}
+		aiUserExpire, err := strconv.Atoi(aiUserExpireStr)
+		if err != nil {
+			resp = models.BaseResponse{Ret: 408, Msg: "请重新授权!", ErrMsg: "expire 异常"}
+			isOk = false
+			return
+		}
+		// 将毫秒时间戳转换为秒和纳秒
+		seconds := int64(aiUserExpire) / 1000
+		nanoseconds := (int64(aiUserExpire) % 1000) * 1e6
+
+		aiUserExpireTime := time.Unix(seconds, nanoseconds)
+		if aiUserExpireTime.Before(time.Now()) {
+			resp = models.BaseResponse{Ret: 408, Msg: "请重新授权!", ErrMsg: "cookie已过期"}
+			isOk = false
+			return
+		}
+	}
+
+	// 获取eta的token
 	authorization := c.Ctx.Input.Header("authorization")
 	if authorization == "" {
 		authorization = c.Ctx.Input.Header("Authorization")
@@ -681,57 +699,60 @@ func checkToken(c *BaseAuthController) (isOk bool, token string, resp models.Bas
 		token = tokenArr[1]
 	}
 
-	// 单点登录逻辑
-	aiUser := c.Ctx.GetCookie("ai_user")
-	if aiUser == `` {
-		aiUser = c.Ctx.GetCookie("ai_token")
-	}
-	fmt.Println("ai_user:", aiUser)
 	//fmt.Println("token:", token)
-	if aiUser != "" {
-		// Token空了, 以Cookie为准重新登录
-		if token == `` {
-			newLogin, e := services.UserLoginChange(aiUser)
-			if e != nil {
-				resp = models.BaseResponse{Ret: 408, Msg: "重登录失败,请稍后重试!", ErrMsg: fmt.Sprint(e)}
-			} else {
-				resp = models.BaseResponse{Ret: models.BaseRespReLoginErr, Msg: "用户切换,请刷新页面", ErrMsg: "user exchanged", Data: newLogin}
-			}
-			isOk = false
-			return
+	// Token空了, 以Cookie为准重新登录
+	if token == `` {
+		newLogin, e := services.UserLoginChange(aiUser)
+		if e != nil {
+			resp = models.BaseResponse{Ret: 408, Msg: "重登录失败,请稍后重试!", ErrMsg: fmt.Sprint(e)}
+		} else {
+			resp = models.BaseResponse{Ret: models.BaseRespReLoginErr, Msg: "用户切换,请刷新页面", ErrMsg: "user exchanged", Data: newLogin}
 		}
+		isOk = false
+		return
+	}
 
-		// todo 将aiUser与session进行关联
-		// token不为空, 那么去校验一下token是否过期, 以及和cookieVal是否匹配
-		// 找不到session, 也直接切CookieValue中的用户登录
-		session, err := system.GetSysSessionByToken(token)
-		if err != nil {
-			newLogin, e := services.UserLoginChange(aiUser)
-			if e != nil {
-				resp = models.BaseResponse{Ret: 408, Msg: "重登录失败,请稍后重试!", ErrMsg: fmt.Sprint(e)}
-			} else {
-				resp = models.BaseResponse{Ret: models.BaseRespReLoginErr, Msg: "用户切换,请刷新页面", ErrMsg: "user exchanged", Data: newLogin}
-			}
-			isOk = false
-			return
+	// todo 将aiUser与session进行关联
+	// token不为空, 那么去校验一下token是否过期, 以及和cookieVal是否匹配
+	// 找不到session, 也直接切CookieValue中的用户登录
+	session, err := system.GetSysSessionByToken(token)
+	if err != nil {
+		newLogin, e := services.UserLoginChange(aiUser)
+		if e != nil {
+			resp = models.BaseResponse{Ret: 408, Msg: "重登录失败,请稍后重试!", ErrMsg: fmt.Sprint(e)}
+		} else {
+			resp = models.BaseResponse{Ret: models.BaseRespReLoginErr, Msg: "用户切换,请刷新页面", ErrMsg: "user exchanged", Data: newLogin}
 		}
+		isOk = false
+		return
+	}
 
-		// CookieVal不匹配、token验证失败、session以及redis中的token过期,那么以cookieVal的用户去登录并返回4014
-		account := utils.MD5(session.UserName)
-		loginKey := fmt.Sprint(utils.CACHE_ACCESS_TOKEN_LOGIN, session.Id)
-		loginCache, _ := utils.Rc.RedisString(loginKey)
-		if session.UserName != aiUser || !utils.CheckToken(account, token) || time.Now().After(session.ExpiredTime) || (session.IsRemember != 1 && loginCache == ``) {
-			newLogin, e := services.UserLoginChange(aiUser)
-			if e != nil {
-				resp = models.BaseResponse{Ret: 408, Msg: "重登录失败,请稍后重试!", ErrMsg: fmt.Sprint(e)}
-			} else {
-				resp = models.BaseResponse{Ret: models.BaseRespReLoginErr, Msg: "用户切换,请刷新页面", ErrMsg: "user exchanged", Data: newLogin}
-			}
-			isOk = false
-			return
+	// CookieVal不匹配、token验证失败、session以及redis中的token过期,那么以cookieVal的用户去登录并返回4014
+	account := utils.MD5(session.UserName)
+	//if session.UserName != aiUser || !utils.CheckToken(account, token) || time.Now().After(session.ExpiredTime){
+	if session.UserName != aiUser || !utils.CheckToken(account, token) {
+		newLogin, e := services.UserLoginChange(aiUser)
+		if e != nil {
+			resp = models.BaseResponse{Ret: 408, Msg: "重登录失败,请稍后重试!", ErrMsg: fmt.Sprint(e)}
+		} else {
+			resp = models.BaseResponse{Ret: models.BaseRespReLoginErr, Msg: "用户切换,请刷新页面", ErrMsg: "user exchanged", Data: newLogin}
 		}
+		isOk = false
+		return
 	}
 
+	//account := utils.MD5(session.UserName)
+	//if !utils.CheckToken(account, token) {
+	//	resp = models.BaseResponse{Ret: 408, Msg: "鉴权失败,请重新登录!", ErrMsg: "登录失效,请重新登陆!,CheckToken Fail"}
+	//	isOk = false
+	//	return
+	//}
+	//if time.Now().After(session.ExpiredTime) {
+	//	resp = models.BaseResponse{Ret: 408, Msg: "请重新登录!", ErrMsg: "获取用户信息异常,Err:" + err.Error()}
+	//	isOk = false
+	//	return
+	//}
+
 	// 正常逻辑
 	if token == "" {
 		resp = models.BaseResponse{Ret: 408, Msg: "请重新授权!", ErrMsg: "请重新授权:Token is empty or account is empty"}

+ 368 - 178
controllers/classify.go

@@ -3,10 +3,14 @@ package controllers
 import (
 	"encoding/json"
 	"eta_gn/eta_api/models"
-	"eta_gn/eta_api/models/report_approve"
+	"eta_gn/eta_api/models/system"
+	"eta_gn/eta_api/models/system/response"
 	"eta_gn/eta_api/services"
 	"eta_gn/eta_api/utils"
 	"fmt"
+	"strconv"
+	"strings"
+	"time"
 )
 
 // 分类
@@ -14,6 +18,7 @@ type ClassifyController struct {
 	BaseAuthController
 }
 
+// Add
 // @Title 新增分类接口
 // @Description 新增分类
 // @Param	request	body models.ClassifyAddReq true "type json string"
@@ -22,6 +27,9 @@ type ClassifyController struct {
 func (this *ClassifyController) Add() {
 	br := new(models.BaseResponse).Init()
 	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
 		this.Data["json"] = br
 		this.ServeJSON()
 	}()
@@ -51,9 +59,26 @@ func (this *ClassifyController) Add() {
 		br.Msg = "分类名称不可为空"
 		return
 	}
+	if req.ClassifyType != utils.ReportTypeDefault && req.ClassifyType != utils.ReportTypePPT {
+		br.Msg = "分类类型有误"
+		br.ErrMsg = fmt.Sprintf("分类类型有误, %d", req.ClassifyType)
+		return
+	}
+	if req.IsRemind == 1 {
+		if req.RemindTime == "" {
+			br.Msg = "请选择报告提醒时间"
+			return
+		}
+		_, e := time.Parse("15:04", req.RemindTime)
+		if e != nil {
+			br.Msg = "提醒时间格式有误"
+			br.ErrMsg = fmt.Sprintf("提醒时间格式有误, %s", req.RemindTime)
+			return
+		}
+	}
 
 	// 新增分类
-	err, errMsg, isSentEmail := services.AddReportClassify(req.ClassifyName, req.ParentId, req.ChartPermissionIdList)
+	err, errMsg, isSentEmail := services.AddReportClassify(req.ClassifyName, req.ParentId, req.ClassifyType, req.IsRemind, req.RemindTime, req.VisibleUserIds)
 	if err != nil {
 		br.Msg = "添加失败"
 		if errMsg != "" {
@@ -69,6 +94,7 @@ func (this *ClassifyController) Add() {
 	br.Msg = "新增成功"
 }
 
+// CheckDeleteClassify
 // @Title 删除分类-检测接口
 // @Description 删除分类-信息检测,是否符合删除条件
 // @Param   ClassifyId   query   int  true       "分类ID"
@@ -85,8 +111,12 @@ func (this *ClassifyController) CheckDeleteClassify() {
 		br.Msg = "参数错误"
 		return
 	}
-	classify, err := models.GetClassifyById(classifyId)
-	if err != nil {
+	classify, e := models.GetClassifyById(classifyId)
+	if e != nil {
+		if utils.IsErrNoRow(e) {
+			br.Msg = "分类不存在,请刷新页面"
+			return
+		}
 		br.Msg = "获取信息失败"
 		br.ErrMsg = "获取信息失败,Err:" + err.Error()
 		return
@@ -102,77 +132,87 @@ func (this *ClassifyController) CheckDeleteClassify() {
 		return
 	}
 
-	//判断分类是否关联了报告
-	if classify.ParentId > 0 {
-		count, err := models.GetReportCountByClassifyId(classifyId)
-		if err != nil {
-			br.Msg = "获取信息失败"
-			br.ErrMsg = "获取信息失败,Err:" + err.Error()
-			return
-		}
-		if count > 0 {
-			resp.Code = 2
-			resp.Msg = "该分类有关联报告,不允许删除"
-			br.Data = resp
-			br.Ret = 200
-			br.Msg = "该分类有关联报告,不允许删除"
-			br.Success = true
-			return
-		}
-	} else {
-		subCount, err := models.GetClassifySubCountByClassifyId(classifyId)
-		if err != nil {
-			br.Msg = "获取信息失败"
-			br.ErrMsg = "获取信息失败,Err:" + err.Error()
-			return
-		}
-		if subCount > 0 {
-			resp.Code = 3
-			resp.Msg = "二级分类有关联报告,不允许删除"
-			br.Data = resp
-			br.Ret = 200
-			br.Msg = "二级分类有关联报告,不允许删除"
-			br.Success = true
-			return
-		}
-		subTotal, err := models.GetClassifySubCountByParentId(classifyId)
-		if err != nil {
-			br.Msg = "获取信息失败"
-			br.ErrMsg = "获取信息失败,Err:" + err.Error()
-			return
-		}
-		if subTotal > 0 {
-			resp.Code = 4
-			resp.Msg = "请先删除该分类下关联分类"
-			br.Data = resp
-			br.Ret = 200
-			br.Msg = "请先删除该分类下关联分类"
-			br.Success = true
-			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.FlowReportTypeChinese, 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 = "该分类关联审批流,不允许删除"
+	if classify.ReportNum > 0 {
+		resp.Code = 2
+		resp.Msg = "该分类或子分类下有关联报告,不允许删除"
 		br.Data = resp
 		br.Ret = 200
-		br.Msg = resp.Msg
+		br.Msg = "该分类或子分类下有关联报告,不允许删除"
 		br.Success = true
 		return
 	}
 
+	//判断分类是否关联了报告
+	//if classify.ParentId > 0 {
+	//	count, err := models.GetReportCountByClassifyId(classifyId)
+	//	if err != nil {
+	//		br.Msg = "获取信息失败"
+	//		br.ErrMsg = "获取信息失败,Err:" + err.Error()
+	//		return
+	//	}
+	//	if count > 0 {
+	//		resp.Code = 2
+	//		resp.Msg = "该分类有关联报告,不允许删除"
+	//		br.Data = resp
+	//		br.Ret = 200
+	//		br.Msg = "该分类有关联报告,不允许删除"
+	//		br.Success = true
+	//		return
+	//	}
+	//} else {
+	//	subCount, err := models.GetClassifySubCountByClassifyId(classifyId)
+	//	if err != nil {
+	//		br.Msg = "获取信息失败"
+	//		br.ErrMsg = "获取信息失败,Err:" + err.Error()
+	//		return
+	//	}
+	//	if subCount > 0 {
+	//		resp.Code = 3
+	//		resp.Msg = "二级分类有关联报告,不允许删除"
+	//		br.Data = resp
+	//		br.Ret = 200
+	//		br.Msg = "二级分类有关联报告,不允许删除"
+	//		br.Success = true
+	//		return
+	//	}
+	//	subTotal, err := models.GetClassifySubCountByParentId(classifyId)
+	//	if err != nil {
+	//		br.Msg = "获取信息失败"
+	//		br.ErrMsg = "获取信息失败,Err:" + err.Error()
+	//		return
+	//	}
+	//	if subTotal > 0 {
+	//		resp.Code = 4
+	//		resp.Msg = "请先删除该分类下关联分类"
+	//		br.Data = resp
+	//		br.Ret = 200
+	//		br.Msg = "请先删除该分类下关联分类"
+	//		br.Success = true
+	//		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.FlowReportTypeChinese, 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
@@ -181,6 +221,7 @@ func (this *ClassifyController) CheckDeleteClassify() {
 	br.Msg = "检测成功"
 }
 
+// Delete
 // @Title 删除分类接口
 // @Description 删除分类
 // @Param	request	body models.DeleteClassifyReq true "type json string"
@@ -204,38 +245,47 @@ func (this *ClassifyController) Delete() {
 		return
 	}
 
-	br.Msg = "报告分类不允许删除"
-	br.IsSendEmail = false
-	return
-
-	item, err := models.GetClassifyById(req.ClassifyId)
-	if err != nil {
-		br.Msg = "获取信息失败"
-		br.ErrMsg = "获取信息失败,Err:" + err.Error()
+	item, e := models.GetClassifyById(req.ClassifyId)
+	if e != nil {
+		if utils.IsErrNoRow(e) {
+			br.Ret = 200
+			br.Success = true
+			br.Msg = "删除成功"
+			return
+		}
+		br.Msg = "删除失败"
+		br.ErrMsg = fmt.Sprintf("获取分类失败, %v", e)
 		return
 	}
-	if item == nil {
-		br.Msg = "分类不存在"
+
+	// 删除校验(上级分类报告数为子分类总和)
+	if item.ReportNum > 0 {
+		br.Msg = "分类下或子分类下有关联报告, 不允许删除"
 		return
 	}
-	err = models.DeleteClassify(req.ClassifyId)
-	if err != nil {
+	var removeIds []int
+	{
+		list, e := models.GetAllClassify()
+		if e != nil {
+			br.Msg = "删除失败"
+			br.ErrMsg = fmt.Sprintf("获取所有分类失败, %v", e)
+			return
+		}
+		removeIds = services.GetClassifyChildIdsTreeRecursive(list, item.Id)
+	}
+	removeIds = append(removeIds, item.Id)
+	if e = models.DeleteClassify(removeIds); e != nil {
 		br.Msg = "删除失败"
-		br.ErrMsg = "删除失败,Err:" + err.Error()
+		br.ErrMsg = fmt.Sprintf("删除分类及子分类失败, %v", e)
 		return
 	}
 
-	// 被删除是二级分类且关联电话会时, 同步FICC活动分类
-	//if item.ParentId > 0 && item.RelateTel == 1 {
-	//	go func() {
-	//		_ = yb.SyncClassifyAndFiccActivityType()
-	//	}()
-	//}
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "删除成功"
 }
 
+// Edit
 // @Title 修改分类接口
 // @Description 修改分类
 // @Param	request	body models.EditClassifyReq true "type json string"
@@ -263,9 +313,21 @@ func (this *ClassifyController) Edit() {
 		br.Msg = "分类名称不可为空"
 		return
 	}
+	if req.IsRemind == 1 {
+		if req.RemindTime == "" {
+			br.Msg = "请选择报告提醒时间"
+			return
+		}
+		_, e := time.Parse("15:04", req.RemindTime)
+		if e != nil {
+			br.Msg = "提醒时间格式有误"
+			br.ErrMsg = fmt.Sprintf("提醒时间格式有误, %s", req.RemindTime)
+			return
+		}
+	}
 
 	// 修改分类
-	err, errMsg, isSentEmail := services.EditReportClassify(req.ClassifyId, req.ClassifyName, req.ChartPermissionIdList)
+	err, errMsg, isSentEmail := services.EditReportClassify(req.ClassifyId, req.ClassifyName, req.IsRemind, req.RemindTime, req.VisibleUserIds)
 	if err != nil {
 		br.Msg = "修改失败"
 		if errMsg != "" {
@@ -376,11 +438,12 @@ func (this *ClassifyController) FindByIdClassify() {
 	br.Msg = "获取成功"
 }
 
+// ListClassify
 // @Title 获取分类列表
 // @Description 获取分类列表
 // @Param   KeyWord   query   string  true       "检索关键词"
-// @Param   CompanyType   query   string  false       "产品类型,枚举值:'ficc','权益';不传默认返回全部"
-// @Param   HideDayWeek   query   int  false       "是否隐藏晨周报"
+// @Param   ClassifyType   query   int  false       "分类类型:0-全部(默认);1-研报;2-PPT"
+// @Param   Enabled   query   int  false       "启用状态:-1-全部(默认);0-禁用;1-启用"
 // @Success 200 {object} models.Classify
 // @router /list [get]
 func (this *ClassifyController) ListClassify() {
@@ -390,6 +453,7 @@ func (this *ClassifyController) ListClassify() {
 		this.ServeJSON()
 	}()
 
+	classifyType, _ := this.GetInt("ClassifyType", 0)
 	keyWord := this.GetString("KeyWord")
 	reqEnabled, _ := this.GetInt("Enabled", -1)
 
@@ -398,110 +462,198 @@ func (this *ClassifyController) ListClassify() {
 		enabled = reqEnabled
 	}
 
-	list, err := models.GetClassifyListByKeyword(keyWord, enabled)
+	//list, err := models.GetClassifyListByKeyword(keyWord, enabled, classifyType)
+	//if err != nil {
+	//	br.Msg = "获取失败"
+	//	br.ErrMsg = "获取失败,Err:" + err.Error()
+	//	return
+	//}
+	//
+	//if keyWord != `` {
+	//	idMap := make(map[int]bool)
+	//
+	//	currParentClassifyIdList := make([]int, 0)
+	//	for _, v := range list {
+	//		idMap[v.Id] = true
+	//		if v.ParentId > 0 {
+	//			currParentClassifyIdList = append(currParentClassifyIdList, v.ParentId)
+	//		}
+	//	}
+	//
+	//	findList := list
+	//	list = make([]*models.ClassifyList, 0)
+	//
+	//	tmpList, tmpErr := services.GetParentClassifyListByParentIdList(currParentClassifyIdList)
+	//	if tmpErr != nil {
+	//		br.Msg = "获取失败"
+	//		br.ErrMsg = "获取上级分类信息失败,Err:" + tmpErr.Error()
+	//		return
+	//	}
+	//	for _, v := range tmpList {
+	//		if _, ok := idMap[v.Id]; !ok {
+	//			list = append(list, v)
+	//		}
+	//	}
+	//
+	//	list = append(list, findList...)
+	//}
+	//
+	//classifyIdList := make([]int, 0)
+	//for i := range list {
+	//	classifyIdList = append(classifyIdList, list[i].Id)
+	//}
+	//parentIdLen := len(classifyIdList)
+	//if parentIdLen == 0 {
+	//	resp := &models.ClassifyListResp{
+	//		List: list,
+	//	}
+	//	br.Data = resp
+	//	br.Ret = 200
+	//	br.Success = true
+	//	br.Msg = "获取成功"
+	//	return
+	//}
+	//
+	//// 获取子目录列表
+	//menuListMap := make(map[int][]*models.ClassifyMenu, 0)
+	//var menuCond string
+	//var menuPars []interface{}
+	//menuCond += ` AND classify_id IN (` + utils.GetOrmInReplace(parentIdLen) + `)`
+	//menuPars = append(menuPars, classifyIdList)
+	//parentMenus, e := models.GetClassifyMenuList(menuCond, menuPars)
+	//if e != nil {
+	//	br.Msg = "获取失败"
+	//	br.ErrMsg = "获取一级分类子目录列表失败"
+	//	return
+	//}
+	//for i := range parentMenus {
+	//	if menuListMap[parentMenus[i].ClassifyId] == nil {
+	//		menuListMap[parentMenus[i].ClassifyId] = make([]*models.ClassifyMenu, 0)
+	//	}
+	//	menuListMap[parentMenus[i].ClassifyId] = append(menuListMap[parentMenus[i].ClassifyId], parentMenus[i])
+	//}
+	//
+	//// 分类与子目录关联
+	//relateMap := make(map[int]int, 0)
+	//{
+	//	var relateCond string
+	//	var relatePars []interface{}
+	//	relateCond += ` AND classify_id IN (` + utils.GetOrmInReplace(parentIdLen) + `)`
+	//	relatePars = append(relatePars, classifyIdList)
+	//	relates, e := models.GetClassifyMenuRelationList(relateCond, relatePars)
+	//	if e != nil {
+	//		br.Msg = "获取失败"
+	//		br.ErrMsg = "获取二级分类子目录关联失败, Err: " + e.Error()
+	//		return
+	//	}
+	//	for i := range relates {
+	//		relateMap[relates[i].ClassifyId] = relates[i].MenuId
+	//	}
+	//}
+	//
+	//// 查询分类绑定的权限
+	//permissionList, _ := models.GetAllPermissionMapping()
+	//classifyPermissionMap := make(map[int][]int)
+	//if len(permissionList) > 0 {
+	//	for _, v := range permissionList {
+	//		classifyPermissionMap[v.ClassifyId] = append(classifyPermissionMap[v.ClassifyId], v.ChartPermissionId)
+	//	}
+	//}
+	//// 遍历分类并绑定子目录和权限
+	//for i, v := range list {
+	//	list[i].ClassifyMenuList = menuListMap[v.Id]
+	//
+	//	list[i].ClassifyMenuId = relateMap[v.Id]
+	//	if permissionIds, ok := classifyPermissionMap[v.Id]; ok {
+	//		list[i].ChartPermissionIdList = permissionIds
+	//	}
+	//}
+
+	// 获取所有分类
+	originList, err := models.GetClassifyListByKeyword("", enabled, 0)
 	if err != nil {
 		br.Msg = "获取失败"
 		br.ErrMsg = "获取失败,Err:" + err.Error()
 		return
 	}
+	list := originList
 
-	if keyWord != `` {
-		idMap := make(map[int]bool)
-
-		currParentClassifyIdList := make([]int, 0)
-		for _, v := range list {
-			idMap[v.Id] = true
-			if v.ParentId > 0 {
-				currParentClassifyIdList = append(currParentClassifyIdList, v.ParentId)
-			}
-		}
-
-		findList := list
-		list = make([]*models.ClassifyList, 0)
-
-		tmpList, tmpErr := services.GetParentClassifyListByParentIdList(currParentClassifyIdList)
-		if tmpErr != nil {
-			br.Msg = "获取失败"
-			br.ErrMsg = "获取上级分类信息失败,Err:" + tmpErr.Error()
-			return
-		}
-		for _, v := range tmpList {
-			if _, ok := idMap[v.Id]; !ok {
-				list = append(list, v)
-			}
-		}
-
-		list = append(list, findList...)
+	visibleUsers, err := models.GetClassifyVisibleAll()
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
 	}
+	visibleUserMap := make(map[int][]int)
 
-	classifyIdList := make([]int, 0)
-	for i := range list {
-		classifyIdList = append(classifyIdList, list[i].Id)
-	}
-	parentIdLen := len(classifyIdList)
-	if parentIdLen == 0 {
-		resp := &models.ClassifyListResp{
-			List: list,
-		}
-		br.Data = resp
-		br.Ret = 200
-		br.Success = true
-		br.Msg = "获取成功"
-		return
+	for _, v := range visibleUsers {
+		visibleUserMap[v.ClassifyId] = append(visibleUserMap[v.ClassifyId], v.AdminId)
 	}
 
-	// 获取子目录列表
-	menuListMap := make(map[int][]*models.ClassifyMenu, 0)
-	var menuCond string
-	var menuPars []interface{}
-	menuCond += ` AND classify_id IN (` + utils.GetOrmInReplace(parentIdLen) + `)`
-	menuPars = append(menuPars, classifyIdList)
-	parentMenus, e := models.GetClassifyMenuList(menuCond, menuPars)
-	if e != nil {
-		br.Msg = "获取失败"
-		br.ErrMsg = "获取一级分类子目录列表失败"
-		return
+	for i, v := range list {
+		list[i].VisiableUsers = visibleUserMap[v.Id]
 	}
-	for i := range parentMenus {
-		if menuListMap[parentMenus[i].ClassifyId] == nil {
-			menuListMap[parentMenus[i].ClassifyId] = make([]*models.ClassifyMenu, 0)
+	// 指定分类类型(上级中的分类类型可能与最下层的不一致,但是要把上级也一起取出来, 这需求...=_=!)
+	if classifyType > 0 {
+		list = make([]*models.ClassifyList, 0)
+		// 先取出指定分类以及他对应的LevelPath中的所有ID
+		classifyIds := make([]int, 0)
+		for _, v := range originList {
+			if v.ClassifyType != classifyType {
+				continue
+			}
+			pathArr := strings.Split(v.LevelPath, ",")
+			for _, p := range pathArr {
+				id, _ := strconv.Atoi(p)
+				if id <= 0 {
+					continue
+				}
+				if utils.InArrayByInt(classifyIds, id) {
+					continue
+				}
+				classifyIds = append(classifyIds, id)
+			}
 		}
-		menuListMap[parentMenus[i].ClassifyId] = append(menuListMap[parentMenus[i].ClassifyId], parentMenus[i])
-	}
 
-	// 分类与子目录关联
-	relateMap := make(map[int]int, 0)
-	{
-		var relateCond string
-		var relatePars []interface{}
-		relateCond += ` AND classify_id IN (` + utils.GetOrmInReplace(parentIdLen) + `)`
-		relatePars = append(relatePars, classifyIdList)
-		relates, e := models.GetClassifyMenuRelationList(relateCond, relatePars)
-		if e != nil {
-			br.Msg = "获取失败"
-			br.ErrMsg = "获取二级分类子目录关联失败, Err: " + e.Error()
-			return
-		}
-		for i := range relates {
-			relateMap[relates[i].ClassifyId] = relates[i].MenuId
+		// 过滤掉不在ID中的
+		for _, v := range originList {
+			if !utils.InArrayByInt(classifyIds, v.Id) {
+				continue
+			}
+			if visible, ok := visibleUserMap[v.Id]; ok {
+				if !utils.InArrayByInt(visible, this.SysUser.AdminId) {
+					continue
+				}
+			}
+			list = append(list, v)
 		}
 	}
 
-	// 查询分类绑定的权限
-	permissionList, _ := models.GetAllPermissionMapping()
-	classifyPermissionMap := make(map[int][]int)
-	if len(permissionList) > 0 {
-		for _, v := range permissionList {
-			classifyPermissionMap[v.ClassifyId] = append(classifyPermissionMap[v.ClassifyId], v.ChartPermissionId)
+	// 有关键词,那么按照上面的方法再筛一遍
+	keyWord = strings.TrimSpace(keyWord)
+	if keyWord != `` {
+		classifyIds := make([]int, 0)
+		for _, v := range list {
+			if !strings.Contains(v.ClassifyName, keyWord) {
+				continue
+			}
+			pathArr := strings.Split(v.LevelPath, ",")
+			for _, p := range pathArr {
+				id, _ := strconv.Atoi(p)
+				if id <= 0 {
+					continue
+				}
+				if utils.InArrayByInt(classifyIds, id) {
+					continue
+				}
+				classifyIds = append(classifyIds, id)
+			}
 		}
-	}
-	// 遍历分类并绑定子目录和权限
-	for i, v := range list {
-		list[i].ClassifyMenuList = menuListMap[v.Id]
-
-		list[i].ClassifyMenuId = relateMap[v.Id]
-		if permissionIds, ok := classifyPermissionMap[v.Id]; ok {
-			list[i].ChartPermissionIdList = permissionIds
+		for _, v := range originList {
+			if !utils.InArrayByInt(classifyIds, v.Id) {
+				continue
+			}
+			list = append(list, v)
 		}
 	}
 
@@ -509,6 +661,8 @@ func (this *ClassifyController) ListClassify() {
 	services.SortClassifyListBySortAndCreateTime(list)
 	// 接着转换结构
 	list = services.GetClassifyListTreeRecursive(list, 0)
+	// 过滤掉没有子目录的分类
+	list = services.RecursiveFilterNoChildTreeClassify(list)
 
 	resp := new(models.ClassifyListResp)
 	resp.List = list
@@ -860,3 +1014,39 @@ func (this *ClassifyController) ClassifyPermissionV2() {
 	br.Msg = "获取成功"
 	br.Data = resp
 }
+
+// AdminList
+// @Title 用户列表
+// @Description 用户列表
+// @Success 200 {object} company.PermissionSetResp
+// @router /user/list [get]
+func (this *ClassifyController) AdminList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	adminList, err := system.GetSysAdminListAll()
+	if err != nil {
+		br.Msg = "获取管理员列表失败"
+		br.ErrMsg = "获取管理员列表失败, Err: " + err.Error()
+	}
+
+	list := make([]*response.AdminItem, 0)
+	for _, v := range adminList {
+		item := new(response.AdminItem)
+		item.AdminId = v.AdminId
+		item.AdminName = v.AdminName
+		item.RealName = v.RealName
+		list = append(list, item)
+	}
+
+	resp := new(response.AdminListResp)
+	resp.List = list
+
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}

+ 42 - 276
controllers/data_manage/chart_info.go

@@ -167,6 +167,38 @@ func (this *ChartInfoController) ChartInfoAdd() {
 		return
 	}
 
+	// 来源字数校验
+	type SourcesFrom struct {
+		Text string `json:"text"`
+	}
+
+	if req.SourcesFrom != "" && len(req.SourcesFrom) > 0 {
+		var sourcesFrom SourcesFrom
+		err = json.Unmarshal([]byte(req.SourcesFrom), &sourcesFrom)
+		if err != nil {
+			return
+		}
+		if len(sourcesFrom.Text) > 50 {
+			br.Msg = "字数已达上限!"
+			br.ErrMsg = "来源文本字数已达上限,请修改!"
+			return
+		}
+	}
+
+	// 说明字数校验
+	if req.Instructions != "" && len(req.Instructions) > 0 {
+		var instructions SourcesFrom
+		err = json.Unmarshal([]byte(req.Instructions), &instructions)
+		if err != nil {
+			return
+		}
+		if len(instructions.Text) > 100 {
+			br.Msg = "字数已达上限!"
+			br.ErrMsg = "说明文本字数已达上限,请修改!"
+			return
+		}
+	}
+
 	chartInfo, err, errMsg, isSendEmail := data.AddChartInfo(req, sysUser.AdminId, sysUser.RealName, this.Lang)
 	if err != nil {
 		br.Msg = "保存失败"
@@ -3552,282 +3584,6 @@ func (this *EdbInfoController) GetBatchChartRefreshResult() {
 
 }
 
-//
-//修复数据时间戳
-
-//func init() {
-//	fmt.Println("start")
-//	edbInfoList,err:=data_manage.GetEdbInfo()
-//	if err!=nil {
-//		fmt.Println("Err:",err.Error())
-//		return
-//	}
-//	startDate:=time.Now().AddDate(-40,0,0).Format(utils.FormatDate)
-//	endDate:=time.Now().Format(utils.FormatDate)
-//	for _,v:=range edbInfoList{
-//		dataList,err:=data_manage.GetEdbDataList(v.Source,v.EdbInfoId,startDate,endDate)
-//		if err!=nil {
-//			fmt.Println("GetEdbDataList Err:",err.Error())
-//			return
-//		}
-//		for _,dv:=range dataList{
-//			dataTime,err:=time.Parse(utils.FormatDate,dv.DataTime)
-//			if err!=nil {
-//				fmt.Println("time.Parse Err:"+err.Error())
-//				return
-//			}
-//			timestamp:=dataTime.UnixNano()/1e6
-//			err=data_manage.ModifyEdbDatadTimestamp(v.Source,dv.EdbDataId,timestamp)
-//			if err!=nil{
-//				fmt.Println("ModifyEdbDatadTimestamp Err:"+err.Error())
-//				return
-//			}
-//			fmt.Println(v.Source,dv.EdbDataId,timestamp)
-//		}
-//	}
-//	//time.Sleep(2*time.Minute)
-//
-//	//startDate:=time.Now().AddDate(-30,0,0).Format(utils.FormatDate)
-//	//endDate:=time.Now().Format(utils.FormatDate)
-//	//dataList,err:=data_manage.GetEdbDataList(1,100099,startDate,endDate)
-//	//if err!=nil {
-//	//	fmt.Println("GetEdbDataList Err:",err.Error())
-//	//	return
-//	//}
-//	//for _,dv:=range dataList{
-//	//	dataTime,err:=time.Parse(utils.FormatDate,dv.DataTime)
-//	//	if err!=nil {
-//	//		fmt.Println("time.Parse Err:"+err.Error())
-//	//		return
-//	//	}
-//	//	timestamp:=dataTime.UnixNano()/1e6
-//	//	err=data_manage.ModifyEdbDatadTimestamp(2,dv.EdbDataId,timestamp)
-//	//	if err!=nil{
-//	//		fmt.Println("ModifyEdbDatadTimestamp Err:"+err.Error())
-//	//		return
-//	//	}
-//	//	fmt.Println(2,dv.EdbDataId,timestamp)
-//	//}
-//
-//	fmt.Println("end")
-//}
-
-//修复指标配置数据
-//func init() {
-//	fmt.Println("start")
-//	edbInfo, err := data_manage.GetEdbInfoAll()
-//	fmt.Println(err)
-//	for k, v := range edbInfoAll {
-//		fmt.Println(k, v.EdbInfoId)
-//		item, err := data_manage.GetEdbInfoMaxAndMinInfo(v.Source, v.EdbCode)
-//		fmt.Println(item, err)
-//		err = data_manage.ModifyEdbInfoMaxAndMinInfo(v.EdbInfoId, item)
-//		if err != nil {
-//			fmt.Println("ModifyEdbInfoMaxAndMinInfo Err:" + err.Error())
-//		}
-//	}
-//	fmt.Println("end")
-//}
-
-//func init() {
-//	fmt.Println("start")
-//	var yearArr []int
-//	yearArr = append(yearArr, 2021)
-//	yearArr = append(yearArr, 2010)
-//	yearArr = append(yearArr, 2020)
-//	yearArr = append(yearArr, 2017)
-//	yearArr = append(yearArr, 2019)
-//	sort.Sort(sort.Reverse(sort.IntSlice(yearArr)))
-//	fmt.Println(yearArr)
-//	fmt.Println("end")
-//}
-
-//季度指标数据计算(公历转农历)
-//func init() {
-//	fmt.Println("start AddCalculateQuarter")
-//	list:=make([]*data_manage.EdbDataList,0)
-//	data_manage.AddCalculateQuarterV4(list)
-//	fmt.Println("end AddCalculateQuarter")
-//	//return
-//}
-
-/*
-{
-  "ChartClassifyId": 58,
-  "ChartName": "公历转农历6",
-  "ChartType": 2,
-  "ChartEdbInfoList": [
-    {
-      "ChartColor": "#00f",
-      "ChartStyle": "spline",
-      "ChartWidth": 3,
-      "EdbInfoId": 100466,
-      "EdbInfoType": 1,
-      "IsAxis": 1,
-      "IsOrder": false,
-      "LeadUnit": "",
-      "LeadValue": 0,
-      "MaxData": 0.2943,
-      "MinData": -0.2448
-    }
-  ]
-}
-*/
-//func init() {
-//	fmt.Println("start")
-//	data_manage.AddCalculateQuarter(100466,6,"C2108252836")
-//	fmt.Println("end")
-//}
-
-//func init() {
-//	data.AddAllChartInfo()
-//}
-
-// 截面散点示例数据
-//func init() {
-//
-//	seriesList := []data_manage.SectionScatterSeriesItemReq{
-//		{
-//			Name:  "系列1名称",
-//			Color: "红红火火色",
-//			EdbList: []data_manage.SectionScatterEdbItemReq{
-//				{
-//					XEdbInfoId: 1,
-//					YEdbInfoId: 2,
-//					Name:       "破铜",
-//					NameEn:     "Po Tong",
-//					XDateType:  1,
-//					XDate:      "",
-//					XDateValue: 0,
-//					YDateType:  2,
-//					YDate:      "",
-//					YDateValue: 30,
-//					IsShow:     false,
-//				},
-//				{
-//					XEdbInfoId: 3,
-//					YEdbInfoId: 4,
-//					Name:       "烂铁",
-//					NameEn:     "Lan Tie",
-//					XDateType:  2,
-//					XDate:      "",
-//					XDateValue: 30,
-//					YDateType:  3,
-//					YDate:      "2023-02-17",
-//					YDateValue: 0,
-//					IsShow:     false,
-//				},
-//			},
-//			ShowLine:     false,
-//			ShowEquation: false,
-//			ShowR:        false,
-//		},
-//		{
-//			Name:  "系列2名称",
-//			Color: "恍恍惚惚色",
-//			EdbList: []data_manage.SectionScatterEdbItemReq{
-//				{
-//					XEdbInfoId: 1,
-//					YEdbInfoId: 2,
-//					Name:       "破铜",
-//					NameEn:     "Po Tong",
-//					XDateType:  3,
-//					XDate:      "2023-02-17",
-//					XDateValue: 0,
-//					YDateType:  2,
-//					YDate:      "",
-//					YDateValue: 60,
-//					IsShow:     true,
-//				},
-//				{
-//					XEdbInfoId: 3,
-//					YEdbInfoId: 4,
-//					Name:       "烂铁",
-//					NameEn:     "Lan Tie",
-//					XDateType:  3,
-//					XDate:      "2023-02-17",
-//					XDateValue: 0,
-//					YDateType:  2,
-//					YDate:      "",
-//					YDateValue: 60,
-//					IsShow:     false,
-//				},
-//			},
-//			ShowLine:     false,
-//			ShowEquation: false,
-//			ShowR:        false,
-//		},
-//	}
-//	rel := data_manage.SectionScatter{
-//		XName:       "X轴名称",
-//		XNameEn:     "X english name",
-//		XUnitName:   "X轴单位",
-//		XUnitNameEn: "X unit english name",
-//		YName:       "Y轴名称",
-//		YNameEn:     "Y english name",
-//		YUnitName:   "Y轴单位",
-//		YUnitNameEn: "Y unit english name",
-//		SeriesList:  seriesList,
-//	}
-//	jsonByte, _ := json.Marshal(rel)
-//	utils.FileLog.Info(string(jsonByte))
-//}
-
-//	func init() {
-//		var a, b float64
-//
-//		testX := []float64{1, 3, 8, 7, 9}
-//		testY := []float64{10, 12, 24, 21, 34}
-//		{
-//			coordinateData := make([]utils.Coordinate, 0)
-//			for k, x := range testX {
-//				tmpCoordinate1 := utils.Coordinate{
-//					X: x,
-//					Y: testY[k],
-//				}
-//				coordinateData = append(coordinateData, tmpCoordinate1)
-//			}
-//			a, b = utils.GetLinearResult(coordinateData)
-//			trendLine := fmt.Sprintf("y=%sx+%s", utils.SubFloatToString(a, 4), utils.SubFloatToString(b, 4))
-//
-//			fmt.Println(trendLine)
-//		}
-//
-//		{
-//			r2 := utils.CalculationDecisive(testX, testY)
-//			fmt.Println(r2)
-//		}
-//	}
-
-// FixChart 修复季节性图的日期数据
-//func FixChart() {
-//	var condition string
-//	var pars []interface{}
-//
-//	// 普通图表
-//	condition += ` AND chart_type = ? AND season_start_date != "" `
-//	pars = append(pars, 2)
-//
-//	//获取图表信息
-//	list, err := data_manage.GetChartInfoListByCondition(condition, pars, 0, 10000)
-//	if err != nil {
-//		fmt.Println("err:", err)
-//		return
-//	}
-//
-//	for _, v := range list {
-//		fmt.Println(v)
-//		v.SeasonStartDate = v.SeasonStartDate + "-01"
-//		v.SeasonEndDate = v.SeasonEndDate + "-12"
-//		err = v.Update([]string{"SeasonStartDate", "SeasonEndDate"})
-//		if err != nil {
-//			fmt.Println(v.ChartInfoId, ";", v.ChartName, "修改失敗")
-//		}
-//		data.EsAddOrEditChartInfo(v.ChartInfoId)
-//		data.EsAddOrEditMyChartInfoByChartInfoId(v.ChartInfoId)
-//	}
-//}
-
 // PreviewRadarChartInfo
 // @Title 图表-获取预览的雷达图
 // @Description 图表-获取预览的雷达图
@@ -4489,3 +4245,13 @@ func (this *ChartInfoController) ChartInfoImgSetBySvg() {
 	br.Data = resp
 	return
 }
+
+// 修复ES中的指标和图表数据
+//func init() {
+//	// 更新ES中的指标数据
+//	data.AddOrEditAllEdbInfoToEs()
+//	// 更新es中的图表数据
+//	data.AddAllChartInfo()
+//
+//	fmt.Println("全部es数据修复完成")
+//}

+ 1 - 1
controllers/data_manage/cross_variety/variety.go

@@ -114,7 +114,7 @@ func (c *VarietyController) Edit() {
 		br.ErrMsg = "添加失败,Err:" + err.Error()
 		return
 	}
-	if item != nil && item.ChartVarietyId != req.ChartVarietyId {
+	if item != nil && item.ChartVarietyId > 0 && item.ChartVarietyId != req.ChartVarietyId {
 		br.Msg = "添加失败,品种名称不能重复"
 		br.IsSendEmail = false
 		return

+ 1 - 1
controllers/data_manage/edb_info_calculate.go

@@ -1486,7 +1486,7 @@ func (this *EdbInfoController) QueryEdbDataTable() {
 	if edbInfo.Source == utils.DATA_SOURCE_BUSINESS && utils.UseMongo {
 		templateStr = fmt.Sprintf("# 查询条件\nquery = {\"edb_code\": \"%s\"}\n# 排序\nsort = [(\"data_time\", -1)]  # -1 表示降序排列,对应 SQL 的 DESC\nprojection = {\"data_time\": 1, \"value\": 1, \"_id\": 0}  # 只选择data_time和value字段,忽略_id字段\n# 使用 find() 方法获取数据,然后使用 aggregate() 转换为列表\nraw_data = list(collection.find(query, projection).sort(sort))\n# 将结果转换为 DataFrame\nraw = pd.DataFrame(raw_data)\n# 转换data_time字段为本地时区时间\nraw['data_time'] = raw['data_time'].apply(lambda x: x.replace(tzinfo=utc_tz)).dt.tz_convert(local_tz).dt.strftime('%s')", edbInfo.EdbCode, "%Y-%m-%d")
 	} else {
-		templateStr = fmt.Sprintf("sql1 = f\"\"\"SELECT data_time,`value` FROM %s WHERE edb_code = '%s' ORDER BY data_time DESC;\"\"\"\nraw = pandas_fetch_all(sql1, db)", tableName, edbInfo.EdbCode)
+		templateStr = fmt.Sprintf("sql1 = f\"\"\"SELECT data_time,\"value\" FROM %s WHERE edb_code = '%s' ORDER BY data_time DESC;\"\"\"\nraw = pandas_fetch_all(sql1, db)", tableName, edbInfo.EdbCode)
 	}
 	info := data_manage.TableInfoResp{
 		ColumnList:  columnList,

+ 3 - 2
controllers/data_manage/excel/balance_table.go

@@ -16,13 +16,14 @@ import (
 	excel2 "eta_gn/eta_api/services/excel"
 	"eta_gn/eta_api/utils"
 	"fmt"
-	"github.com/tealeg/xlsx"
 	"io/ioutil"
 	"os"
 	"sort"
 	"strconv"
 	"strings"
 	"time"
+
+	"github.com/tealeg/xlsx"
 )
 
 // GetChildTable
@@ -1397,7 +1398,7 @@ func downloadBalanceTable(excelInfo *excel.ExcelInfo, lang string) (savePath, zi
 				errMsg = msg
 				return
 			}
-			tableData, er := excel2.GetTableDataByMixedTableData(newResult, false)
+			tableData, er := excel2.GetTableDataByMixedTableData(newResult, false, childExcelInfo.ExcelInfoId)
 			if er != nil {
 				errMsg = "获取失败"
 				err = fmt.Errorf("转换成table失败,Err:" + err.Error())

+ 26 - 9
controllers/data_manage/excel/excel_info.go

@@ -107,6 +107,24 @@ func (c *ExcelInfoController) Add() {
 		}
 	}
 
+	// 来源字数校验
+	type SourcesFrom struct {
+		Text string `json:"text"`
+	}
+
+	if req.SourcesFrom != "" && len(req.SourcesFrom) > 0 {
+		var sourcesFrom SourcesFrom
+		err = json.Unmarshal([]byte(req.SourcesFrom), &sourcesFrom)
+		if err != nil {
+			return
+		}
+		if len(sourcesFrom.Text) > 50 {
+			br.Msg = "字数已达上限!"
+			br.ErrMsg = "来源文本字数已达上限,请修改!"
+			return
+		}
+	}
+
 	if req.ParentId > 0 {
 		parentExcelInfo, e := excel3.GetExcelInfoById(req.ParentId)
 		if e != nil {
@@ -1617,7 +1635,7 @@ func (c *ExcelInfoController) GetExcelTableData() {
 			br.ErrMsg = "获取最新的数据失败,Err:" + err.Error()
 			return
 		}
-		tableData, err = excel.GetTableDataByMixedTableData(newResult, true)
+		tableData, err = excel.GetTableDataByMixedTableData(newResult, true, excelInfo.ExcelInfoId)
 		if err != nil {
 			br.Msg = "获取失败"
 			br.ErrMsg = "转换成table失败,Err:" + err.Error()
@@ -1646,7 +1664,7 @@ func (c *ExcelInfoController) GetExcelTableData() {
 	}
 
 	tableData = excel.HandleTableCell(tableData)
-	tableData, err = excel.HandleRuleToTableCell(excelInfo.ExcelInfoId, tableData)
+	// tableData, err = excel.HandleRuleToTableCell(excelInfo.ExcelInfoId, tableData)
 	if err != nil {
 		utils.FileLog.Info("表格管理规则处理失败,HandleRuleToTableCell err:", err.Error())
 	}
@@ -2625,7 +2643,7 @@ func (c *ExcelInfoController) Download() {
 			br.ErrMsg = "获取最新的数据失败,Err:" + err.Error()
 			return
 		}
-		tableData, err = excel.GetTableDataByMixedTableData(newResult, false)
+		tableData, err = excel.GetTableDataByMixedTableData(newResult, false, excelInfo.ExcelInfoId)
 		if err != nil {
 			br.Msg = "获取失败"
 			br.ErrMsg = "转换成table失败,Err:" + err.Error()
@@ -3207,12 +3225,11 @@ func (c *ExcelInfoController) EditExcelRule() {
 		br.Msg = "应用选区不能为空"
 		return
 	}
-	if req.FontColor == "" {
-		br.Msg = "字体颜色不能为空"
-		return
-	}
-	if req.BackgroundColor == "" {
-		br.Msg = "背景颜色不能为空"
+
+	req.FontColor = strings.TrimSpace(req.FontColor)
+	req.BackgroundColor = strings.TrimSpace(req.BackgroundColor)
+	if req.FontColor == "" && req.BackgroundColor == "" {
+		br.Msg = "字体颜色和背景颜色不能同时为空"
 		return
 	}
 	if req.RuleType == 3 && req.RightValue == "" {

+ 100 - 5
controllers/data_manage/manual.go

@@ -6,7 +6,6 @@ import (
 	"eta_gn/eta_api/models"
 	"eta_gn/eta_api/models/data_manage"
 	"eta_gn/eta_api/models/system"
-	"eta_gn/eta_api/services/data"
 	"eta_gn/eta_api/utils"
 	"fmt"
 	"strconv"
@@ -37,16 +36,112 @@ func (this *ManualController) ManualSysUserSearch() {
 		return
 	}
 	keyWord := this.GetString("KeyWord")
-	list, err := data.GetManualSysUser(keyWord)
-	if err != nil {
+	//list, err := data.GetManualSysUser(keyWord)
+	//if err != nil {
+	//	br.Msg = "获取失败"
+	//	br.ErrMsg = "获取失败,Err:" + err.Error()
+	//	return
+	//}
+
+	resp := make([]*data_manage.ManualSysUser, 0)
+	departments, e := system.GetSysDepartmentAll()
+	if e != nil {
 		br.Msg = "获取失败"
-		br.ErrMsg = "获取失败,Err:" + err.Error()
+		br.ErrMsg = fmt.Sprintf("获取部门失败, %v", e)
+		return
+	}
+	groups, e := system.GetSysGroupList()
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("获取分组失败, %v", e)
 		return
 	}
+	users := make([]*system.AdminItem, 0)
+	{
+		var cond string
+		var pars []interface{}
+		if keyWord != "" {
+			cond += ` AND (real_name LIKE ? OR admin_name LIKE ? OR mobile LIKE ? )  `
+			pars = utils.GetLikeKeywordPars(pars, keyWord, 3)
+		}
+		sysUsers, e := system.GetSysUserItems(cond, pars)
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("获取用户失败, %v", e)
+			return
+		}
+		users = sysUsers
+	}
+
+	// 用户组
+	groupUser := make(map[int][]*data_manage.ManualSysUser)
+	departNullUser := make(map[int][]*data_manage.ManualSysUser) // 部门下无分组的用户
+	for _, v := range users {
+		t := new(data_manage.ManualSysUser)
+		t.ItemId = v.AdminId
+		t.ItemName = v.RealName
+		if v.GroupId == 0 {
+			if departNullUser[v.DepartmentId] == nil {
+				departNullUser[v.DepartmentId] = make([]*data_manage.ManualSysUser, 0)
+			}
+			departNullUser[v.DepartmentId] = append(departNullUser[v.DepartmentId], t)
+			continue
+		}
+		if groupUser[v.GroupId] == nil {
+			groupUser[v.GroupId] = make([]*data_manage.ManualSysUser, 0)
+		}
+		groupUser[v.GroupId] = append(groupUser[v.GroupId], t)
+	}
+
+	// 部门分组
+	departmentGroup := make(map[int][]*data_manage.ManualSysUser)
+	for _, v := range groups {
+		if v.ParentId > 0 {
+			continue
+		}
+		group := new(data_manage.ManualSysUser)
+		group.ItemId = v.DepartmentId * 100000
+		group.ItemName = v.GroupName
+		gu := groupUser[v.GroupId]
+		if len(gu) == 0 {
+			continue
+		}
+		group.Children = gu
+		if departmentGroup[v.DepartmentId] == nil {
+			departmentGroup[v.DepartmentId] = make([]*data_manage.ManualSysUser, 0)
+		}
+		departmentGroup[v.DepartmentId] = append(departmentGroup[v.DepartmentId], group)
+	}
+
+	// 合并数据
+	for _, v := range departments {
+		department := new(data_manage.ManualSysUser)
+		department.ItemId = v.DepartmentId * 10000
+		department.ItemName = v.DepartmentName
+		dg := departmentGroup[v.DepartmentId]
+		if len(dg) > 0 {
+			department.Children = dg
+		} else {
+			// 未分组
+			group := new(data_manage.ManualSysUser)
+			group.ItemId = v.DepartmentId * 100000
+			group.ItemName = "无分组"
+			du := departNullUser[v.DepartmentId]
+			if du != nil && len(du) > 0 {
+				group.Children = du
+				department.Children = append(department.Children, group)
+			}
+		}
+		if len(department.Children) == 0 {
+			continue
+		}
+		resp = append(resp, department)
+	}
+
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "获取成功"
-	br.Data = list
+	br.Data = resp
 }
 
 // @Title 获取手工数据权限用户

+ 0 - 67
controllers/data_manage/predict_edb_info.go

@@ -1865,73 +1865,6 @@ func (this *PredictEdbInfoController) Modify() {
 	br.IsAddLog = true
 }
 
-//func init() {
-//	r := new(regression.Regression)
-//	r.SetObserved("Murders per annum per 1,000,000 inhabitants")
-//	r.SetVar(0, "Inhabitants")
-//	r.SetVar(1, "Percent with incomes below $5000")
-//	r.SetVar(2, "Percent unemployed")
-//	r.Train(
-//		regression.DataPoint(11.2, []float64{587000, 16.5, 6.2}),
-//		regression.DataPoint(13.4, []float64{643000, 20.5, 6.4}),
-//		regression.DataPoint(40.7, []float64{635000, 26.3, 9.3}),
-//		regression.DataPoint(5.3, []float64{692000, 16.5, 5.3}),
-//		regression.DataPoint(24.8, []float64{1248000, 19.2, 7.3}),
-//		regression.DataPoint(12.7, []float64{643000, 16.5, 5.9}),
-//		regression.DataPoint(20.9, []float64{1964000, 20.2, 6.4}),
-//		regression.DataPoint(35.7, []float64{1531000, 21.3, 7.6}),
-//		regression.DataPoint(8.7, []float64{713000, 17.2, 4.9}),
-//		regression.DataPoint(9.6, []float64{749000, 14.3, 6.4}),
-//		regression.DataPoint(14.5, []float64{7895000, 18.1, 6}),
-//		regression.DataPoint(26.9, []float64{762000, 23.1, 7.4}),
-//		regression.DataPoint(15.7, []float64{2793000, 19.1, 5.8}),
-//		regression.DataPoint(36.2, []float64{741000, 24.7, 8.6}),
-//		regression.DataPoint(18.1, []float64{625000, 18.6, 6.5}),
-//		regression.DataPoint(28.9, []float64{854000, 24.9, 8.3}),
-//		regression.DataPoint(14.9, []float64{716000, 17.9, 6.7}),
-//		regression.DataPoint(25.8, []float64{921000, 22.4, 8.6}),
-//		regression.DataPoint(21.7, []float64{595000, 20.2, 8.4}),
-//		regression.DataPoint(25.7, []float64{3353000, 16.9, 6.7}),
-//	)
-//	r.Run()
-//
-//	fmt.Printf("Regression formula:\n%v\n", r.Formula)
-//	//fmt.Printf("Regression:\n%s\n", r)
-//}
-
-//func init() {
-//	//data := []float64{4080, 4070, 4070, 4020, 4020, 4010, 3950, 3980, 4000, 3990, 4020}
-//	//median, _ := stats.Median(data)
-//	//fmt.Println(median) // 3.65
-//	//stats.LinearRegression()
-//
-//	//data := []float64{ , , , , , , , , }
-//
-//	data := []Coordinate{
-//		{1, 4080},
-//		{2, 4070},
-//		{3, 4070},
-//		{4, 4020},
-//		{5, 4020},
-//		{6, 4010},
-//		{7, 3950},
-//		{8, 3980},
-//		{9, 4000},
-//		{10, 3990},
-//		{11, 4020},
-//	}
-//
-//	//r, _ := ct(data)
-//
-//	ct(data)
-//	//fmt.Println(r)
-//
-//}
-
-//func init() {
-//	data.AddOrEditAllEdbInfoToEs()
-//}
-
 // ClassifyEdbInfoItems
 // @Title 获取分类下指标接口
 // @Description 获取分类下指标接口

+ 19 - 0
controllers/data_manage/range_analysis/chart_info.go

@@ -183,6 +183,25 @@ func (this *RangeChartChartInfoController) Add() {
 		br.ErrMsg = "参数解析失败,Err:" + err.Error()
 		return
 	}
+
+	// 来源字数校验
+	type SourcesFrom struct {
+		Text string `json:"text"`
+	}
+
+	if req.SourcesFrom != "" && len(req.SourcesFrom) > 0 {
+		var sourcesFrom SourcesFrom
+		err = json.Unmarshal([]byte(req.SourcesFrom), &sourcesFrom)
+		if err != nil {
+			return
+		}
+		if len(sourcesFrom.Text) > 50 {
+			br.Msg = "字数已达上限!"
+			br.ErrMsg = "来源文本字数已达上限,请修改!"
+			return
+		}
+	}
+
 	if req.DateType == 0 {
 		req.DateType = 3
 	}

+ 1 - 1
controllers/data_manage/supply_analysis/variety.go

@@ -224,7 +224,7 @@ func (this *VarietyController) Edit() {
 		br.ErrMsg = "添加失败,Err:" + err.Error()
 		return
 	}
-	if item != nil && item.VarietyId != req.VarietyId {
+	if item != nil && item.VarietyId > 0 && item.VarietyId != req.VarietyId {
 		br.Msg = "添加失败,品种名称不能重复"
 		br.IsSendEmail = false
 		return

+ 300 - 0
controllers/image_conf_controller.go

@@ -0,0 +1,300 @@
+package controllers
+
+import (
+	"encoding/json"
+	"eta_gn/eta_api/models"
+	"eta_gn/eta_api/utils"
+	"github.com/rdlucklib/rdluck_tools/paging"
+)
+
+type ImageConfController struct {
+	BaseAuthController
+}
+
+// GetImageMaterial
+// @Title 根据条件查询图片素材
+// @Description 根据条件查询图片素材
+// @Success 200 {object} models.ImageConf
+// @router /get/image/material [get]
+func (this *ImageConfController) GetImageMaterial() {
+	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
+	}
+
+	confType, err := this.GetInt("ConfType")
+	if err != nil {
+		return
+	}
+	if confType <= 0 {
+		br.Msg = "请选择配置类型!"
+		br.ErrMsg = "请选择配置类型!"
+		return
+	}
+	imageType, _ := this.GetInt("ImageType")
+
+	imageName := this.GetString("ImageName")
+
+	pageSize, _ := this.GetInt("PageSize")
+	currentIndex, _ := this.GetInt("CurrentIndex")
+
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize := paging.StartIndex(currentIndex, pageSize)
+
+	var condition string
+	var pars []interface{}
+
+	condition += ` conf_type=? `
+	pars = append(pars, confType)
+
+	if imageType > 0 {
+		condition += ` AND image_type=? `
+		pars = append(pars, imageType)
+	}
+
+	if imageName != "" {
+		condition += ` AND image_name LIKE '%` + imageName + `%' `
+	}
+
+	count, err := models.GetImageConfByConditionCount(condition, pars)
+	if err != nil {
+		return
+	}
+	pagingItem := paging.GetPaging(currentIndex, pageSize, int(count))
+	var ImageConfPage models.ImageConfPage
+	ImageConfPage.Paging = pagingItem
+
+	if count <= 0 {
+		ImageConfPage.List = []*models.ImageConf{}
+
+		br.Msg = "操作成功"
+		br.Ret = 200
+		br.Data = ImageConfPage
+		return
+	}
+
+	condition += ` ORDER BY create_time asc `
+
+	condition += ` LIMIT ?, ? `
+	pars = append(pars, startSize, pageSize)
+
+	imageConfList, err := models.GetImageConfByCondition(condition, pars)
+
+	ImageConfPage.List = imageConfList
+
+	br.Msg = "操作成功"
+	br.Ret = 200
+	br.Data = ImageConfPage
+	return
+}
+
+// AddImageMaterial
+// @Title 新增图片素材
+// @Description 新增图片素材
+// @Success 200
+// @router /add/image/material [post]
+func (this *ImageConfController) AddImageMaterial() {
+	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 []*models.ImageConf
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+
+	// 校验图片名称是否重复
+	for _, item := range req {
+		if item.ImageType <= 0 {
+			br.Msg = "请选择图片类型!"
+			br.ErrMsg = "请选择图片类型!"
+			return
+		}
+
+		if item.Url == "" {
+			br.Msg = "请上传图片!"
+			br.ErrMsg = "请上传图片!"
+			return
+		}
+
+		// 校验名称是否重复
+		imageConfByName, err := models.GetImageConfByName(item.ImageName)
+		if err != nil {
+			return
+		}
+		if imageConfByName != nil {
+			br.Msg = "图片名称已存在,请重新上传!"
+			br.ErrMsg = "图片名称已存在,请重新上传!"
+			return
+		}
+	}
+
+	err := models.BatchAddImageMaterials(req, len(req))
+	if err != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "操作失败,Err:" + err.Error()
+		return
+	}
+
+	br.Msg = "操作成功"
+	br.Ret = 200
+	return
+}
+
+// EditImageMaterial
+// @Title 修改图片素材
+// @Description 修改图片素材
+// @Success 200
+// @router /edit/image/material [post]
+func (this *ImageConfController) EditImageMaterial() {
+	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 *models.ImageConf
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+
+	if req.ImageType <= 0 {
+		br.Msg = "请选择图片类型!"
+		br.ErrMsg = "请选择图片类型!"
+		return
+	}
+
+	if req.Url == "" {
+		br.Msg = "请上传图片!"
+		br.ErrMsg = "请上传图片!"
+		return
+	}
+
+	// 校验名称是否重复
+	imageConfByName, err := models.GetImageConfByName(req.ImageName)
+	if err != nil {
+		return
+	}
+	if imageConfByName != nil && imageConfByName.ImageConfId != req.ImageConfId {
+		br.Msg = "图片名称已存在,请重新上传!"
+		br.ErrMsg = "图片名称已存在,请重新上传!"
+		return
+	}
+
+	imageConf, err := models.GetImageConfById(req.ImageConfId)
+	if err != nil {
+		return
+	}
+	if imageConf.ImageConfId == 0 {
+		br.Msg = "该素材已被删除"
+		br.ErrMsg = "操作失败,Err: 该素材已被删除"
+		return
+	}
+
+	err = models.EditImageMaterial(req)
+	if err != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "操作失败,Err:" + err.Error()
+		return
+	}
+
+	br.Msg = "操作成功"
+	br.Ret = 200
+	return
+}
+
+// DeleteImageMaterial
+// @Title 删除图片素材
+// @Description 删除图片素材
+// @Success 200
+// @router /delete/image/material [post]
+func (this *ImageConfController) DeleteImageMaterial() {
+	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
+	}
+	type DeleteReq struct {
+		ImageConfId int `json:"ImageConfId"`
+	}
+	var req []DeleteReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+
+	var imageConfIds []int
+	for _, deleteReq := range req {
+		// 查看图片是否被使用
+		count, err := models.GetPptByImageIdCount(deleteReq.ImageConfId)
+		if err != nil {
+			return
+		}
+		if count > 0 {
+			br.Msg = "图片已被使用,不允许删除"
+			br.ErrMsg = "图片已被使用,不允许删除"
+			return
+		}
+		imageConfIds = append(imageConfIds, deleteReq.ImageConfId)
+	}
+
+	err := models.DeleteImageConfByIds(imageConfIds)
+	if err != nil {
+		return
+	}
+
+	br.Msg = "操作成功"
+	br.Ret = 200
+	return
+}

+ 647 - 0
controllers/ppt_report.go

@@ -0,0 +1,647 @@
+package controllers
+
+import (
+	"encoding/json"
+	"eta_gn/eta_api/models"
+	"eta_gn/eta_api/models/system"
+	"eta_gn/eta_api/services"
+	"eta_gn/eta_api/utils"
+	"fmt"
+	"strconv"
+	"strings"
+	"time"
+
+	"github.com/rdlucklib/rdluck_tools/paging"
+)
+
+// ReportClassify
+// @Title 获取ppt报告分类
+// @Description 获取ppt报告分类
+// @Param   Source   query   int   false   "来源:1-我的;2-协作;3-公共"
+// @Success 200 {object} models.PptReportClassifyItem
+// @router /report/classify [get]
+func (this *PptV2Controller) ReportClassify() {
+	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
+	}
+	source, _ := this.GetInt("Source", 1)
+	if source < 1 || source > 3 {
+		source = 1
+	}
+
+	// 获取PPT, source:1-我的;2-协作;3-公共
+	pptList := make([]*models.PptV2, 0)
+	{
+		cond := ``
+		pars := make([]interface{}, 0)
+		switch source {
+		case 1:
+			cond += ` AND admin_id = ?`
+			pars = append(pars, sysUser.AdminId)
+		case 2:
+			cond += ` AND collaborate_type = ? AND (admin_id = ? OR FIND_IN_SET(?, collaborate_users)) `
+			pars = append(pars, utils.ReportWriteTypeGroup, sysUser.AdminId)
+		case 3:
+			cond += ` AND report_source = ? AND state = ?`
+			pars = append(pars, utils.ReportSourceOuter, models.ReportStatePass)
+		}
+		pptOb := new(models.PptV2)
+		list, e := pptOb.GetItemsByCondition(cond, pars, models.PptReportQueryFields, "modify_time DESC")
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("获取PPT失败, %v", e)
+			return
+		}
+		pptList = list
+	}
+	classifyPpt := make(map[int][]*models.PptReportItem)
+	for _, v := range pptList {
+		// 当前编辑人
+		t := v.Format2ReportItem(v)
+		editor, e := services.UpdatePptEditing(v.PptId, 0, sysUser.AdminId, sysUser.RealName, false)
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("获取PPT编辑状态失败, err: %s", e.Error())
+			return
+		}
+		t.Editor = editor
+
+		// 权限
+		if source == 1 || source == 2 {
+			t.HasAuth = true
+		} else {
+			if v.AdminId == sysUser.AdminId {
+				t.HasAuth = true
+			}
+			if t.HasAuth == false && v.CollaborateUsers != "" {
+				authorArr := strings.Split(v.CollaborateUsers, ",")
+				strId := strconv.Itoa(sysUser.AdminId)
+				if utils.InArrayByStr(authorArr, strId) {
+					t.HasAuth = true
+				}
+			}
+		}
+
+		if classifyPpt[v.ClassifyId] == nil {
+			classifyPpt[v.ClassifyId] = make([]*models.PptReportItem, 0)
+		}
+		classifyPpt[v.ClassifyId] = append(classifyPpt[v.ClassifyId], t)
+	}
+
+	visibleUsers, err := models.GetClassifyVisibleAll()
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("获取可见用户失败, err: %s", err.Error())
+		return
+	}
+	visibleUsersMap := make(map[int][]int)
+	for _, v := range visibleUsers {
+		visibleUsersMap[v.ClassifyId] = append(visibleUsersMap[v.ClassifyId], v.AdminId)
+	}
+
+	var resp []*models.PptReportClassifyItem
+	// 获取分类
+	classifies := make([]*models.Classify, 0)
+	{
+		// 获取所有分类
+		ob := new(models.Classify)
+		cond := ` AND enabled = ?`
+		pars := make([]interface{}, 0)
+		pars = append(pars, 1, utils.ReportTypePPT)
+		list, e := ob.GetItemsByCondition(cond, pars, []string{}, "sort ASC, create_time ASC")
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("获取分类失败, %v", e)
+			return
+		}
+
+		// 上级中的分类类型可能与最下层的不一致,但是要把上级也一起取出来, 所以这里要过滤一遍
+		classifyIds := make([]int, 0)
+		for _, v := range list {
+			if v.ClassifyType != utils.ReportTypePPT {
+				continue
+			}
+			// 没有PPT的分类也都过滤掉
+			//if len(classifyPpt[v.Id]) == 0 {
+			//	continue
+			//}
+
+			// 根据LevelPath去处理
+			pathArr := strings.Split(v.LevelPath, ",")
+			for _, p := range pathArr {
+				id, _ := strconv.Atoi(p)
+				if id <= 0 {
+					continue
+				}
+				if utils.InArrayByInt(classifyIds, id) {
+					continue
+				}
+				classifyIds = append(classifyIds, id)
+			}
+		}
+
+		// 过滤掉不在ID中的
+		for _, v := range list {
+			if !utils.InArrayByInt(classifyIds, v.Id) {
+				continue
+			}
+			if visible, ok := visibleUsersMap[v.Id]; ok {
+				if !utils.InArrayByInt(visible, sysUser.AdminId) {
+					continue
+				}
+			}
+			classifies = append(classifies, v)
+		}
+	}
+
+	resp = services.GetPptReportClassifyTreeRecursive(classifies, 0, classifyPpt)
+	resp = services.RecursiveFilterPptNoChildTreeClassify(resp)
+
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// ReportList
+// @Title 获取ppt报告列表-分页
+// @Description 获取ppt报告列表-分页
+// @Param   Source   query   int   false   "来源:1-我的;2-协作;3-公共"
+// @Param   ClassifyId   query   int   false   "分类ID"
+// @Param   Keyword   query   string   false   "搜索关键词"
+// @Success 200 {object} models.PptPageReportResp
+// @router /report/list [get]
+func (this *PptV2Controller) ReportList() {
+	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
+	}
+	pageSize, _ := this.GetInt("PageSize")
+	currentIndex, _ := this.GetInt("CurrentIndex")
+	source, _ := this.GetInt("Source", 1)
+	if source < 1 || source > 3 {
+		source = 1
+	}
+	classifyId, _ := this.GetInt("ClassifyId", 0)
+	keyword := this.GetString("Keyword")
+
+	var startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = utils.StartIndex(currentIndex, pageSize)
+	resp := new(models.PptPageReportResp)
+	resp.List = make([]*models.PptReportItem, 0)
+
+	// 获取PPT, source:1-我的;2-协作;3-公共
+	pptList := make([]*models.PptV2, 0)
+	cond := ``
+	pars := make([]interface{}, 0)
+	{
+		switch source {
+		case 1:
+			cond += ` AND admin_id = ?`
+			pars = append(pars, sysUser.AdminId)
+		case 2:
+			cond += ` AND collaborate_type = ? AND (admin_id = ? OR FIND_IN_SET(?, collaborate_users)) `
+			pars = append(pars, utils.ReportWriteTypeGroup, sysUser.AdminId, sysUser.AdminId)
+		case 3:
+			cond += ` AND report_source = ? AND state = ?`
+			pars = append(pars, utils.ReportSourceOuter, models.ReportStatePass)
+		}
+		if classifyId > 0 {
+			// 查询分类及子集
+			classifyOb := new(models.Classify)
+			childCond := ` AND FIND_IN_SET(?, level_path)`
+			childPars := make([]interface{}, 0)
+			childPars = append(childPars, classifyId)
+			children, e := classifyOb.GetItemsByCondition(childCond, childPars, []string{"id"}, "")
+			if e != nil {
+				br.Msg = "获取失败"
+				br.ErrMsg = fmt.Sprintf("获取分类及子分类失败, %v", e)
+				return
+			}
+			var childIds []int
+			for _, v := range children {
+				childIds = append(childIds, v.Id)
+			}
+			if len(childIds) == 0 {
+				page := paging.GetPaging(currentIndex, pageSize, 0)
+				resp.Paging = page
+				br.Data = resp
+				br.Ret = 200
+				br.Success = true
+				br.Msg = "获取成功"
+				return
+			}
+			cond += ` AND classify_id IN (?)`
+			pars = append(pars, childIds)
+		}
+		keyword = strings.TrimSpace(keyword)
+		if keyword != "" {
+			cond += ` AND title LIKE ?`
+			pars = append(pars, fmt.Sprint("%", keyword, "%"))
+		}
+	}
+	noVisibleClassifyIds, err := models.GetNoVisibleClassifyIdByAdminId(this.SysUser.AdminId)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取不可见分类id失败,Err:" + err.Error()
+		return
+	}
+	if len(noVisibleClassifyIds) > 0 {
+		cond += ` AND classify_id NOT IN (?) `
+		pars = append(pars, noVisibleClassifyIds)
+	}
+	pptOb := new(models.PptV2)
+	total, e := pptOb.GetCountByCondition(cond, pars)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("获取PPT总数失败, %v", e)
+		return
+	}
+	list, e := pptOb.GetPageItemsByCondition(cond, pars, models.PptReportQueryFields, "modify_time DESC", startSize, pageSize)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("获取PPT失败, %v", e)
+		return
+	}
+	pptList = list
+
+	for _, v := range pptList {
+		// 当前编辑人
+		t := v.Format2ReportItem(v)
+		editor, e := services.UpdatePptEditing(v.PptId, 0, sysUser.AdminId, sysUser.RealName, false)
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("获取PPT编辑状态失败, err: %s", e.Error())
+			return
+		}
+		t.Editor = editor
+
+		// 权限
+		if source == 1 || source == 2 {
+			t.HasAuth = true
+		} else {
+			if v.AdminId == sysUser.AdminId {
+				t.HasAuth = true
+			}
+			if t.HasAuth == false && v.CollaborateUsers != "" {
+				authorArr := strings.Split(v.CollaborateUsers, ",")
+				strId := strconv.Itoa(sysUser.AdminId)
+				if utils.InArrayByStr(authorArr, strId) {
+					t.HasAuth = true
+				}
+			}
+		}
+
+		resp.List = append(resp.List, t)
+	}
+
+	page := paging.GetPaging(currentIndex, pageSize, total)
+	resp.Paging = page
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// CreateReport
+// @Title 新增ppt报告
+// @Description 新增ppt报告
+// @Param	request	body models.PptReportCreateReq true "type json string"
+// @Success 200 Ret=200 新增成功
+// @router /report/add [post]
+func (this *PptV2Controller) CreateReport() {
+	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.PptReportCreateReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常"
+		br.ErrMsg = fmt.Sprintf("参数解析失败, %v", e)
+		return
+	}
+	req.Title = strings.TrimSpace(req.Title)
+	if req.Title == "" {
+		br.Msg = "请输入标题"
+		return
+	}
+	if req.AddType != 1 && req.AddType != 2 {
+		req.AddType = 1
+	}
+	if req.ClassifyId <= 0 {
+		br.Msg = "请选择分类"
+		return
+	}
+	if req.CollaborateType != 1 && req.CollaborateType != 2 {
+		br.Msg = "协作方式异常"
+		br.ErrMsg = fmt.Sprintf("协作方式异常, %d", req.CollaborateType)
+		return
+	}
+	if req.CollaborateType == 2 && len(req.CollaborateUserIds) == 0 {
+		br.Msg = "请选择协作人"
+		return
+	}
+
+	// 新报告
+	newItem := new(models.PptV2)
+	newItem.Title = req.Title
+	newItem.AddType = req.AddType
+	newItem.ClassifyId = req.ClassifyId
+	newItem.CollaborateType = req.CollaborateType
+	if len(req.CollaborateUserIds) > 0 {
+		var partnerArr []string
+		for _, v := range req.CollaborateUserIds {
+			partnerArr = append(partnerArr, strconv.Itoa(v))
+		}
+		newItem.CollaborateUsers = strings.Trim(strings.Join(partnerArr, ","), `"`)
+	}
+	newItem.PptVersion = 2
+	newItem.AdminId = sysUser.AdminId
+	newItem.AdminRealName = sysUser.RealName
+	newItem.ReportSource = utils.ReportSourceLocal // 固定本地PPT
+	newItem.State = models.ReportStateUnpublished  // 默认未发布
+	newItem.CreateTime = time.Now()
+	newItem.ModifyTime = time.Now()
+
+	// 继承PPT内容
+	if req.AddType == utils.ReportAddTypeInherit && req.InheritPptId > 0 {
+		inheritPpt, e := models.GetPptV2ById(req.InheritPptId)
+		if e != nil {
+			if utils.IsErrNoRow(e) {
+				br.Msg = "被继承报告不存在"
+				return
+			}
+			br.Msg = "操作失败"
+			br.ErrMsg = fmt.Sprintf("操作失败, %v", e)
+			return
+		}
+		newItem.TemplateType = inheritPpt.TemplateType
+		newItem.BackgroundImg = inheritPpt.BackgroundImg
+		newItem.CurrentBackgroundImg = inheritPpt.CurrentBackgroundImg
+		newItem.BackCoverImg = inheritPpt.BackCoverImg
+		newItem.CurrentBackgroundImgId = inheritPpt.CurrentBackgroundImgId
+		newItem.BackCoverImgId = inheritPpt.BackCoverImgId
+		newItem.BackgroundImgId = inheritPpt.BackgroundImgId
+		newItem.ReportType = inheritPpt.ReportType
+		newItem.PptDate = inheritPpt.PptDate
+		newItem.Content = inheritPpt.Content
+		newItem.CoverContent = inheritPpt.CoverContent
+		newItem.TitleSetting = inheritPpt.TitleSetting
+	}
+
+	// 非继承PPT,有内容时写入内容(如合并PPT时)
+	if req.InheritPptId <= 0 {
+		newItem.TemplateType = req.FirstPage.TemplateType
+		newItem.BackgroundImg = req.FirstPage.ImgUrl
+		newItem.CurrentBackgroundImg = req.FirstPage.CurrentBackgroundImg
+		newItem.BackCoverImg = req.FirstPage.BackCoverImg
+		newItem.CurrentBackgroundImgId = req.FirstPage.CurrentBackgroundImgId
+		newItem.BackCoverImgId = req.FirstPage.BackCoverImgId
+		newItem.BackgroundImgId = req.FirstPage.BackgroundImgId
+		newItem.ReportType = req.FirstPage.ReportType
+		newItem.PptDate = req.FirstPage.PptDate
+		newItem.Content = req.Content
+		newItem.CoverContent = req.CoverContent
+		newItem.TitleSetting = req.TitleSetting
+	}
+
+	// 初始的PPT页数
+	if newItem.Content != "" {
+		var pageContents []services.PPTContent
+		_ = json.Unmarshal([]byte(newItem.Content), &pageContents) // 这里转不过去问题也不大,编辑时自动保存会更新页数字段
+		newItem.PptPage = len(pageContents)
+	}
+
+	newId, e := models.AddPptV2(newItem)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("新增PPT报告失败, %v", e)
+		return
+	}
+
+	// 更新报告分类计数
+	go func() {
+		_ = services.UpdateClassifyReportNum(req.ClassifyId)
+	}()
+
+	resp := models.AddPptResp{
+		PptId: newId,
+	}
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// AuthList
+// @Title 获取有权限的列表
+// @Description 获取有权限的列表
+// @Param   PageSize   query   int  true       "每页数据条数"
+// @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
+// @Param   Keyword   query   string  false       "搜索关键词"
+// @Param   ClassifyId   query   int  false       "分类ID"
+// @Success 200 {object} models.PptPageReportResp
+// @router /report/auth_list [get]
+func (this *PptV2Controller) AuthList() {
+	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
+	}
+	pageSize, _ := this.GetInt("PageSize")
+	currentIndex, _ := this.GetInt("CurrentIndex")
+	keyword := this.GetString("Keyword")
+	classifyId, _ := this.GetInt("ClassifyId", 0)
+
+	var startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = utils.StartIndex(currentIndex, pageSize)
+
+	var pptList []*models.PptReportItem
+	// 无相关搜索时,返回空集(其实我感觉没必要,没关键词查全部也属于正常=_=!)
+	if keyword == `` && classifyId <= 0 {
+		page := paging.GetPaging(currentIndex, pageSize, 0)
+		resp := new(models.PptPageReportResp)
+		resp.Paging = page
+		resp.List = pptList
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "获取成功"
+		br.Data = resp
+		return
+	}
+
+	// 查询自己创建的以及协作人包含自己的报告、以及外部已审批报告
+	var cond string
+	var pars []interface{}
+	cond += ` AND (admin_id = ? OR (admin_id <> ? AND FIND_IN_SET(?, collaborate_users)) OR (report_source = ? AND state = ?))`
+	pars = append(pars, sysUser.AdminId, sysUser.AdminId, sysUser.AdminId, utils.ReportSourceOuter, models.ReportStatePass)
+	if classifyId > 0 {
+		cond += ` AND classify_id = ? `
+		pars = append(pars, classifyId)
+	}
+	keyword = strings.TrimSpace(keyword)
+	if keyword != `` {
+		cond += ` AND title LIKE ? `
+		pars = utils.GetLikeKeywordPars(pars, keyword, 1)
+	}
+	noVisisbleClassifyIds, err := models.GetNoVisibleClassifyIdByAdminId(sysUser.AdminId)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("获取不可见分类失败, %v", err)
+		return
+	}
+	if len(noVisisbleClassifyIds) > 0 {
+		cond += ` AND classify_id NOT IN (?)`
+		pars = append(pars, noVisisbleClassifyIds)
+	}
+
+	pptOb := new(models.PptV2)
+	total, e := pptOb.GetCountByCondition(cond, pars)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("获取PPT总数失败, %v", e)
+		return
+	}
+	list, e := pptOb.GetPageItemsByCondition(cond, pars, models.PptReportQueryFields, "", startSize, pageSize)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("获取PPT失败, %v", e)
+		return
+	}
+
+	// 分类完整路径、协作人姓名
+	classifyIdFull := make(map[int]string)
+	{
+		ob := new(models.Classify)
+		classifies, e := ob.GetItemsByCondition("", make([]interface{}, 0), []string{}, "")
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("获取分类失败, %v", e)
+			return
+		}
+		classifyIdName := make(map[string]string)
+		for _, v := range classifies {
+			classifyIdName[strconv.Itoa(v.Id)] = v.ClassifyName
+		}
+		for _, v := range classifies {
+			arr := strings.Split(v.LevelPath, ",")
+			if len(arr) == 0 {
+				continue
+			}
+			var nameArr []string
+			for _, a := range arr {
+				n := classifyIdName[a]
+				if n == "" {
+					continue
+				}
+				nameArr = append(nameArr, n)
+			}
+			classifyIdFull[v.Id] = strings.Join(nameArr, "/")
+		}
+	}
+	adminIdName := make(map[int]string)
+	{
+		cond := ` AND enabled = 1`
+		pars := make([]interface{}, 0)
+		sysAdmin, e := system.GetSysAdminList(cond, pars, []string{}, "")
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取用户失败,Err:" + e.Error()
+			return
+		}
+		for _, v := range sysAdmin {
+			adminIdName[v.AdminId] = v.RealName
+		}
+	}
+
+	// 格式化数据
+	for _, v := range list {
+		t := v.Format2ReportItem(v)
+		t.HasAuth = true                              // 该列表固定有权限
+		t.FullClassify = classifyIdFull[v.ClassifyId] // 分类的完整路径
+
+		// 协作人
+		if v.CollaborateUsers != "" {
+			var authors []models.PptReportCollaborateUser
+			authorArr := strings.Split(v.CollaborateUsers, ",")
+			for _, au := range authorArr {
+				uid, _ := strconv.Atoi(au)
+				if uid <= 0 {
+					continue
+				}
+				name := adminIdName[uid]
+				if name == "" {
+					continue
+				}
+				authors = append(authors, models.PptReportCollaborateUser{
+					AdminId:  uid,
+					RealName: name,
+				})
+			}
+			t.CollaborateUsers = authors
+		}
+		pptList = append(pptList, t)
+	}
+
+	page := paging.GetPaging(currentIndex, pageSize, total)
+	resp := new(models.PptPageReportResp)
+	resp.Paging = page
+	resp.List = pptList
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}

+ 317 - 140
controllers/ppt_v2.go

@@ -7,6 +7,7 @@ import (
 	"eta_gn/eta_api/services"
 	"eta_gn/eta_api/services/ppt"
 	"eta_gn/eta_api/utils"
+	"fmt"
 	_ "image/gif"
 	_ "image/jpeg"
 	_ "image/png"
@@ -132,32 +133,37 @@ func (this *PptV2Controller) AddPpt() {
 			br.Msg = "请输入目录ID"
 			return
 		}
-		item, err := models.GetPptV2ByTitleAndId(req.FirstPage.Title, this.SysUser.AdminId)
-		if err != nil && !utils.IsErrNoRow(err) {
-			br.Msg = "获取数据异常!"
-			br.ErrMsg = "获取数据异常,Err:" + err.Error()
-			return
-		}
-		if item != nil && item.PptId > 0 && item.PptId != int(req.PptId) {
-			br.Msg = "标题已存在,不可重复添加"
-			br.IsSendEmail = false
-			return
-		}
+		//item, err := models.GetPptV2ByTitleAndId(req.FirstPage.Title, this.SysUser.AdminId)
+		//if err != nil && !utils.IsErrNoRow(err) {
+		//	br.Msg = "获取数据异常!"
+		//	br.ErrMsg = "获取数据异常,Err:" + err.Error()
+		//	return
+		//}
+		//if item != nil && item.PptId > 0 && item.PptId != int(req.PptId) {
+		//	br.Msg = "标题已存在,不可重复添加"
+		//	br.IsSendEmail = false
+		//	return
+		//}
 		pptInfo := &models.PptV2{
 			//PptId:         0,
-			TemplateType:  req.FirstPage.TemplateType,
-			BackgroundImg: req.FirstPage.ImgUrl,
-			Title:         req.FirstPage.Title,
-			ReportType:    req.FirstPage.ReportType,
-			PptDate:       req.FirstPage.PptDate,
-			Content:       req.Content,
-			CoverContent:  req.CoverContent,
-			CreateTime:    time.Now(),
-			ModifyTime:    time.Now(),
-			AdminId:       this.SysUser.AdminId,
-			AdminRealName: this.SysUser.RealName,
-			PptVersion:    2,
-			TitleSetting:  req.TitleSetting,
+			TemplateType:           req.FirstPage.TemplateType,
+			BackgroundImg:          req.FirstPage.ImgUrl,
+			BackgroundImgId:        req.FirstPage.BackgroundImgId,
+			BackCoverImg:           req.FirstPage.BackCoverImg,
+			BackCoverImgId:         req.FirstPage.BackCoverImgId,
+			CurrentBackgroundImg:   req.FirstPage.CurrentBackgroundImg,
+			CurrentBackgroundImgId: req.FirstPage.CurrentBackgroundImgId,
+			Title:                  req.FirstPage.Title,
+			ReportType:             req.FirstPage.ReportType,
+			PptDate:                req.FirstPage.PptDate,
+			Content:                req.Content,
+			CoverContent:           req.CoverContent,
+			CreateTime:             time.Now(),
+			ModifyTime:             time.Now(),
+			AdminId:                this.SysUser.AdminId,
+			AdminRealName:          this.SysUser.RealName,
+			PptVersion:             2,
+			TitleSetting:           req.TitleSetting,
 		}
 		newId, err = models.AddPptV2(pptInfo)
 		if err != nil {
@@ -183,6 +189,11 @@ func (this *PptV2Controller) AddPpt() {
 		}
 		pptInfo.TemplateType = req.FirstPage.TemplateType
 		pptInfo.BackgroundImg = req.FirstPage.ImgUrl
+		pptInfo.BackgroundImgId = req.FirstPage.BackgroundImgId
+		pptInfo.BackCoverImg = req.FirstPage.BackCoverImg
+		pptInfo.BackCoverImgId = req.FirstPage.BackCoverImgId
+		pptInfo.CurrentBackgroundImg = req.FirstPage.CurrentBackgroundImg
+		pptInfo.CurrentBackgroundImgId = req.FirstPage.CurrentBackgroundImgId
 		pptInfo.Title = req.FirstPage.Title
 		pptInfo.ReportType = req.FirstPage.ReportType
 		pptInfo.PptDate = req.FirstPage.PptDate
@@ -190,7 +201,7 @@ func (this *PptV2Controller) AddPpt() {
 		pptInfo.CoverContent = req.CoverContent
 		pptInfo.ModifyTime = time.Now()
 		pptInfo.TitleSetting = req.TitleSetting
-		err = pptInfo.Update([]string{"TemplateType", "BackgroundImg", "Title", "ReportType", "PptDate", "Content", "ModifyTime", "CoverContent", "TitleSetting"})
+		err = pptInfo.Update([]string{"TemplateType", "BackgroundImg", "BackgroundImgId", "BackCoverImg", "BackCoverImgId", "CurrentBackgroundImg", "CurrentBackgroundImgId", "Title", "ReportType", "PptDate", "Content", "ModifyTime", "CoverContent", "TitleSetting"})
 
 		msg = "保存成功"
 	}
@@ -226,17 +237,7 @@ func (this *PptV2Controller) EditPpt() {
 		br.Msg = "标题不能为空"
 		return
 	}
-	item, err := models.GetPptV2ByTitleAndId(req.FirstPage.Title, this.SysUser.AdminId)
-	if err != nil && !utils.IsErrNoRow(err) {
-		br.Msg = "获取数据异常!"
-		br.ErrMsg = "获取数据异常,Err:" + err.Error()
-		return
-	}
-	if item != nil && item.PptId > 0 && item.PptId != int(req.PptId) {
-		br.Msg = "标题已存在,不可重复添加"
-		br.IsSendEmail = false
-		return
-	}
+
 	pptInfo, err := models.GetPptV2ById(int(req.PptId))
 	if err != nil {
 		br.Msg = "信息获取失败"
@@ -244,18 +245,19 @@ func (this *PptV2Controller) EditPpt() {
 		return
 	}
 
-	// 判断权限
+	// 协作人权限
 	if pptInfo.AdminId != this.SysUser.AdminId {
-		_, err := models.GetPPtGrantConf(pptInfo.PptId, this.SysUser.AdminId)
-		if err != nil {
-			if utils.IsErrNoRow(err) {
-				br.Msg = `该PPT已取消共享,保存失败`
-				br.ErrMsg = `该PPT已取消共享,保存失败`
-				br.IsSendEmail = false
-			} else {
-				br.Msg = `保存失败`
-				br.ErrMsg = `保存失败,ERR:` + err.Error()
-			}
+		if pptInfo.CollaborateType != utils.ReportWriteTypeGroup {
+			br.Msg = "非协作人无权操作"
+			return
+		}
+		if pptInfo.CollaborateUsers == "" {
+			br.Msg = "非协作人无权操作"
+			return
+		}
+		partnerArr := strings.Split(pptInfo.CollaborateUsers, ",")
+		if !utils.InArrayByStr(partnerArr, fmt.Sprint(this.SysUser.AdminId)) {
+			br.Msg = "非协作人无权操作"
 			return
 		}
 	}
@@ -263,6 +265,11 @@ func (this *PptV2Controller) EditPpt() {
 	// 修改
 	pptInfo.TemplateType = req.FirstPage.TemplateType
 	pptInfo.BackgroundImg = req.FirstPage.ImgUrl
+	pptInfo.CurrentBackgroundImg = req.FirstPage.CurrentBackgroundImg
+	pptInfo.BackCoverImg = req.FirstPage.BackCoverImg
+	pptInfo.CurrentBackgroundImgId = req.FirstPage.CurrentBackgroundImgId
+	pptInfo.BackCoverImgId = req.FirstPage.BackCoverImgId
+	pptInfo.BackgroundImgId = req.FirstPage.BackgroundImgId
 	pptInfo.Title = req.FirstPage.Title
 	pptInfo.ReportType = req.FirstPage.ReportType
 	pptInfo.PptDate = req.FirstPage.PptDate
@@ -270,55 +277,77 @@ func (this *PptV2Controller) EditPpt() {
 	pptInfo.CoverContent = req.CoverContent
 	pptInfo.ModifyTime = time.Now()
 	pptInfo.TitleSetting = req.TitleSetting
-	err = pptInfo.Update([]string{"TemplateType", "BackgroundImg", "Title", "ReportType", "PptDate", "Content", "ModifyTime", "CoverContent", "TitleSetting"})
+	err = pptInfo.Update([]string{"TemplateType", "BackgroundImg", "CurrentBackgroundImg", "BackCoverImg", "CurrentBackgroundImgId", "BackCoverImgId", "BackgroundImgId", "Title", "ReportType", "PptDate", "Content", "ModifyTime", "CoverContent", "TitleSetting"})
 	if err != nil {
 		br.Msg = "编辑失败"
 		br.ErrMsg = "编辑失败,Err:" + err.Error()
 		return
 	}
 
-	pptMap, err := models.GetPptMappingByPptId(req.PptId)
-	if err != nil {
-		br.Msg = `该PPT信息不存在, 保存失败`
-		br.ErrMsg = `该PPT信息不存在, 保存失败, Err` + err.Error()
-		br.IsSendEmail = false
-		return
-	}
-	pptMapList, err := models.GetPptMappingListByGroupId(pptMap.GroupId)
-	if err != nil {
-		br.ErrMsg = "PPT目录信息异常"
-		return
+	//pptMap, err := models.GetPptMappingByPptId(req.PptId)
+	//if err != nil {
+	//	br.Msg = `该PPT信息不存在, 保存失败`
+	//	br.ErrMsg = `该PPT信息不存在, 保存失败, Err` + err.Error()
+	//	br.IsSendEmail = false
+	//	return
+	//}
+	//pptMapList, err := models.GetPptMappingListByGroupId(pptMap.GroupId)
+	//if err != nil {
+	//	br.ErrMsg = "PPT目录信息异常"
+	//	return
+	//}
+	//count, err := models.GetPptMappingByGroupPptCountId(pptMap.GroupPptId, this.SysUser.AdminId)
+	//if err != nil {
+	//	br.Msg = "查询映射关系失败"
+	//	br.ErrMsg = "查询映射关系失败, 保存失败, Err:" + err.Error()
+	//	return
+	//}
+	//if !pptMap.IsMoved && len(pptMapList) > 1 && count > 0 {
+	//	// 如果没有人为移动位置, 默认将当前ppt置顶
+	//	err = ppt.MoveGroupPpt(pptMap.GroupId, pptMap.GroupPptId, pptMapList[0].GroupPptId, 0, this.SysUser.AdminId)
+	//	if err != nil {
+	//		br.Msg = err.Error()
+	//		br.ErrMsg = "移动失败,Err:" + err.Error()
+	//		return
+	//	}
+	//}
+	// 版本记录
+	historyInfo := &models.PptV2History{
+		PptId:         pptInfo.PptId,
+		TemplateType:  pptInfo.TemplateType,
+		BackgroundImg: pptInfo.BackgroundImg,
+		Title:         pptInfo.Title,
+		ReportType:    pptInfo.ReportType,
+		PptDate:       pptInfo.PptDate,
+		Content:       pptInfo.Content,
+		CoverContent:  pptInfo.CoverContent,
+		AdminId:       this.SysUser.AdminId,
+		AdminRealName: this.SysUser.RealName,
+		CreateTime:    time.Now(),
+		TitleSetting:  pptInfo.TitleSetting,
 	}
-	count, err := models.GetPptMappingByGroupPptCountId(pptMap.GroupPptId, this.SysUser.AdminId)
+	err = historyInfo.Add()
 	if err != nil {
-		br.Msg = "查询映射关系失败"
-		br.ErrMsg = "查询映射关系失败, 保存失败, Err:" + err.Error()
+		br.Msg = "保存失败"
+		br.ErrMsg = "保存PPT版本失败, Err: " + err.Error()
 		return
 	}
-	if !pptMap.IsMoved && len(pptMapList) > 1 && count > 0 {
-		// 如果没有人为移动位置, 默认将当前ppt置顶
-		err = ppt.MoveGroupPpt(pptMap.GroupId, pptMap.GroupPptId, pptMapList[0].GroupPptId, 0, this.SysUser.AdminId)
-		if err != nil {
-			br.Msg = err.Error()
-			br.ErrMsg = "移动失败,Err:" + err.Error()
-			return
-		}
-	}
-
 	// 日志记录
 	{
 		logInfo := &models.PptV2SaveLog{
-			PptId:         pptInfo.PptId,
-			TemplateType:  pptInfo.TemplateType,
-			BackgroundImg: pptInfo.BackgroundImg,
-			Title:         pptInfo.Title,
-			ReportType:    pptInfo.ReportType,
-			PptDate:       pptInfo.PptDate,
-			Content:       pptInfo.Content,
-			CoverContent:  pptInfo.CoverContent,
-			AdminId:       this.SysUser.AdminId,
-			AdminRealName: this.SysUser.RealName,
-			CreateTime:    time.Now(),
+			PptId:                pptInfo.PptId,
+			TemplateType:         pptInfo.TemplateType,
+			BackgroundImg:        pptInfo.BackgroundImg,
+			CurrentBackgroundImg: pptInfo.CurrentBackgroundImg,
+			BackCoverImg:         pptInfo.BackCoverImg,
+			Title:                pptInfo.Title,
+			ReportType:           pptInfo.ReportType,
+			PptDate:              pptInfo.PptDate,
+			Content:              pptInfo.Content,
+			CoverContent:         pptInfo.CoverContent,
+			AdminId:              this.SysUser.AdminId,
+			AdminRealName:        this.SysUser.RealName,
+			CreateTime:           time.Now(),
 		}
 		_, err = models.AddPptV2SaveLog(logInfo)
 	}
@@ -365,6 +394,11 @@ func (this *PptV2Controller) DeletePpt() {
 		br.Msg = "无权删除"
 		return
 	}
+	if pptInfo.ReportSource != utils.ReportSourceLocal {
+		br.Msg = "仅允许删除系统内部报告"
+		return
+	}
+
 	err = models.DeletePptV2(req.PptId)
 	if err != nil {
 		br.Msg = "删除失败"
@@ -378,6 +412,12 @@ func (this *PptV2Controller) DeletePpt() {
 		br.ErrMsg = "删除失败,Err:" + err.Error()
 		return
 	}
+
+	// 更新报告计数
+	go func() {
+		_ = services.UpdateClassifyReportNum(pptInfo.ClassifyId)
+	}()
+
 	br.Ret = 200
 	br.Success = true
 	br.IsAddLog = true
@@ -415,6 +455,60 @@ func (this *PptV2Controller) DetailPpt() {
 		return
 	}
 
+	// 权限
+	var hasAuth bool
+	if pptInfo.AdminId == sysUser.AdminId {
+		hasAuth = true
+	}
+	if hasAuth == false && pptInfo.CollaborateUsers != "" {
+		authorArr := strings.Split(pptInfo.CollaborateUsers, ",")
+		strId := strconv.Itoa(sysUser.AdminId)
+		if utils.InArrayByStr(authorArr, strId) {
+			hasAuth = true
+		}
+	}
+
+	// 维护背景图
+	if pptInfo.CurrentBackgroundImg == "" {
+		var condition string
+		var pars []interface{}
+
+		condition += ` conf_type=? `
+		pars = append(pars, 1)
+
+		condition += ` AND image_type=? `
+		pars = append(pars, 2)
+
+		condition += ` ORDER BY create_time asc `
+
+		imageConfList, err := models.GetImageConfByCondition(condition, pars)
+
+		if err == nil && len(imageConfList) > 0 {
+			pptInfo.CurrentBackgroundImg = imageConfList[0].Url
+			pptInfo.CurrentBackgroundImgId = imageConfList[0].ImageConfId
+		}
+	}
+	// 维护封面图
+	if pptInfo.BackgroundImg == "" {
+		var condition string
+		var pars []interface{}
+
+		condition += ` conf_type=? `
+		pars = append(pars, 1)
+
+		condition += ` AND image_type=? `
+		pars = append(pars, 1)
+
+		condition += ` ORDER BY create_time asc `
+
+		imageConfList, err := models.GetImageConfByCondition(condition, pars)
+
+		if err == nil && len(imageConfList) > 0 {
+			pptInfo.BackgroundImg = imageConfList[0].Url
+			pptInfo.BackgroundImgId = imageConfList[0].ImageConfId
+		}
+	}
+
 	// 编辑中
 	editor, e := services.UpdatePptEditing(pptId, 0, sysUser.AdminId, sysUser.RealName, false)
 	if e != nil {
@@ -425,6 +519,7 @@ func (this *PptV2Controller) DetailPpt() {
 	resp := new(models.PPTDetailResp)
 	resp.PptV2 = pptInfo
 	resp.Editor = editor
+	resp.HasAuth = hasAuth
 
 	br.Ret = 200
 	br.Success = true
@@ -474,12 +569,12 @@ func (this *PptV2CommonController) DownloadPptx() {
 func (this *PptV2Controller) Publish() {
 	br := new(models.BaseResponse).Init()
 	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
 		this.Data["json"] = br
 		this.ServeJSON()
 	}()
-
-	//SavePptV2PathReq
-	//PptV2PublishReq
 	var req models.SavePptV2PathReq
 	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
 	if err != nil {
@@ -492,12 +587,67 @@ func (this *PptV2Controller) Publish() {
 		br.Msg = "参数错误"
 		return
 	}
-	err = models.EditPptV2Path(pptId, req.PptxUrl)
-	if err != nil {
-		br.Msg = "发布失败"
-		br.ErrMsg = "发布失败,Err:" + err.Error()
+	req.PptxUrl = strings.TrimSpace(req.PptxUrl)
+	if req.PptxUrl == "" {
+		br.Msg = "文件地址为空"
+		return
+	}
+	//err = models.EditPptV2Path(pptId, req.PptxUrl)
+	//if err != nil {
+	//	br.Msg = "发布失败"
+	//	br.ErrMsg = "发布失败,Err:" + err.Error()
+	//	return
+	//}
+
+	pptItem, e := models.GetPptV2ById(pptId)
+	if e != nil {
+		if utils.IsErrNoRow(e) {
+			br.Msg = "PPT不存在, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("获取PPT失败, %v", e)
 		return
 	}
+	stateOrigin := pptItem.State
+
+	var updateCols []string
+	updateCols = append(updateCols, "PptxUrl", "ModifyTime")
+	pptItem.PptxUrl = req.PptxUrl
+	pptItem.ModifyTime = time.Now()
+
+	// 本地PPT(不加限制, 想发布就发布)
+	if pptItem.ReportSource == utils.ReportSourceLocal {
+		updateCols = append(updateCols, "State", "PublishTime")
+		pptItem.State = models.ReportStatePublished
+		pptItem.PublishTime = time.Now()
+	}
+
+	// 外部PPT
+	if pptItem.ReportSource == utils.ReportSourceOuter {
+		// 报告状态
+		if pptItem.OutReportId == "" {
+			br.Msg = "PPT异常"
+			br.ErrMsg = fmt.Sprintf("PPT外部报告ID为空, ID: %d", pptId)
+			return
+		}
+		if pptItem.State != models.ReportStateWaitSubmit && pptItem.State != models.ReportStateRefused {
+			br.Msg = "状态异常, 不允许提交"
+			br.ErrMsg = fmt.Sprintf("PPT报告状态异常, %d", pptItem.State)
+			return
+		}
+		updateCols = append(updateCols, "SubmitTime", "State")
+		pptItem.SubmitTime = time.Now()
+		pptItem.State = models.ReportStateWaitApprove
+	}
+
+	// 更新PPT
+	if e = pptItem.Update(updateCols); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("更新PPT失败, %v", e)
+		return
+	}
+
 	//添加发布记录
 	{
 		record := new(models.PptV2PublishRecord)
@@ -506,6 +656,21 @@ func (this *PptV2Controller) Publish() {
 		record.PptUrl = req.PptxUrl
 		go models.AddPptV2PublishRecord(record)
 	}
+
+	// 回调智力共享审批
+	go func() {
+		if pptItem.ReportSource != utils.ReportSourceOuter {
+			return
+		}
+		outId, _ := strconv.Atoi(pptItem.OutReportId)
+		// 若回调失败, 则恢复提交前状态(先这么处理吧,允许再次提交)
+		e = services.OuterReportCallBack(outId, pptItem.Title, req.PptxUrl, ".pptx")
+		if e != nil {
+			pptItem.State = stateOrigin
+			_ = pptItem.Update([]string{"State"})
+		}
+	}()
+
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "发布成功"
@@ -644,17 +809,17 @@ func (this *PptV2Controller) SaveLog() {
 	}
 
 	// 获取ppt
-	item, err := models.GetPptV2ByTitleAndId(req.FirstPage.Title, this.SysUser.AdminId)
-	if err != nil && !utils.IsErrNoRow(err) {
-		br.Msg = "获取数据异常!"
-		br.ErrMsg = "获取数据异常,Err:" + err.Error()
-		return
-	}
-	if item != nil && item.PptId > 0 && item.PptId != int(req.PptId) {
-		br.Msg = "标题已存在,不可重复添加"
-		br.IsSendEmail = false
-		return
-	}
+	//item, err := models.GetPptV2ByTitleAndId(req.FirstPage.Title, this.SysUser.AdminId)
+	//if err != nil && !utils.IsErrNoRow(err) {
+	//	br.Msg = "获取数据异常!"
+	//	br.ErrMsg = "获取数据异常,Err:" + err.Error()
+	//	return
+	//}
+	//if item != nil && item.PptId > 0 && item.PptId != int(req.PptId) {
+	//	br.Msg = "标题已存在,不可重复添加"
+	//	br.IsSendEmail = false
+	//	return
+	//}
 
 	//变更ppt内容
 	pptItem, e := models.GetPptV2ById(int(req.PptId))
@@ -679,6 +844,11 @@ func (this *PptV2Controller) SaveLog() {
 	}
 	pptItem.TemplateType = req.FirstPage.TemplateType
 	pptItem.BackgroundImg = req.FirstPage.ImgUrl
+	pptItem.CurrentBackgroundImg = req.FirstPage.CurrentBackgroundImg
+	pptItem.BackCoverImg = req.FirstPage.BackCoverImg
+	pptItem.CurrentBackgroundImgId = req.FirstPage.CurrentBackgroundImgId
+	pptItem.BackCoverImgId = req.FirstPage.BackCoverImgId
+	pptItem.BackgroundImgId = req.FirstPage.BackgroundImgId
 	pptItem.Title = req.FirstPage.Title
 	pptItem.ReportType = req.FirstPage.ReportType
 	pptItem.PptDate = req.FirstPage.PptDate
@@ -686,51 +856,58 @@ func (this *PptV2Controller) SaveLog() {
 	pptItem.ModifyTime = time.Now()
 	pptItem.TitleSetting = req.TitleSetting
 	pptItem.PptPage = len(pptContent)
-	err = pptItem.Update([]string{"TemplateType", "BackgroundImg", "Title", "ReportType", "PptDate", "Content", "ModifyTime", "TitleSetting", "ppt_page"})
-
-	// 将更新后的PPT, 置顶
-	pptMap, err := models.GetPptMappingByPptId(int64(req.PptId))
-	if err != nil {
-		br.Msg = `该PPT信息不存在, 保存失败`
-		br.ErrMsg = `该PPT信息不存在, 保存失败, Err` + err.Error()
-		br.IsSendEmail = false
-		return
-	}
-	pptMapList, err := models.GetPptMappingListByGroupId(pptMap.GroupId)
-	if err != nil {
-		br.ErrMsg = "PPT目录信息异常"
-		return
-	}
-	count, err := models.GetPptMappingByGroupPptCountId(pptMap.GroupPptId, this.SysUser.AdminId)
+	err = pptItem.Update([]string{"TemplateType", "BackgroundImg", "CurrentBackgroundImg", "BackCoverImg", "CurrentBackgroundImgId", "BackCoverImgId", "BackgroundImgId", "Title", "ReportType", "PptDate", "Content", "ModifyTime", "TitleSetting", "ppt_page"})
 	if err != nil {
-		br.Msg = "查询映射关系失败"
-		br.ErrMsg = "查询映射关系失败, 保存失败, Err:" + err.Error()
+		br.Msg = "自动保存失败"
+		br.ErrMsg = fmt.Sprintf("自动保存PPT失败, ID: %d, Err: %v", pptItem.PptId, err)
 		return
 	}
-	if !pptMap.IsMoved && len(pptMapList) > 1 && count > 0 {
-		// 如果没有人为移动位置, 且当前用户有权限, 默认将当前ppt置顶
-		err = ppt.MoveGroupPpt(pptMap.GroupId, pptMap.GroupPptId, pptMapList[0].GroupPptId, 0, this.SysUser.AdminId)
-		if err != nil {
-			br.Msg = err.Error()
-			br.ErrMsg = "移动失败,Err:" + err.Error()
-			return
-		}
-	}
+
+	// 将更新后的PPT, 置顶
+	//pptMap, err := models.GetPptMappingByPptId(int64(req.PptId))
+	//if err != nil {
+	//	br.Msg = `该PPT信息不存在, 保存失败`
+	//	br.ErrMsg = `该PPT信息不存在, 保存失败, Err` + err.Error()
+	//	br.IsSendEmail = false
+	//	return
+	//}
+	//pptMapList, err := models.GetPptMappingListByGroupId(pptMap.GroupId)
+	//if err != nil {
+	//	br.ErrMsg = "PPT目录信息异常"
+	//	return
+	//}
+	//count, err := models.GetPptMappingByGroupPptCountId(pptMap.GroupPptId, this.SysUser.AdminId)
+	//if err != nil {
+	//	br.Msg = "查询映射关系失败"
+	//	br.ErrMsg = "查询映射关系失败, 保存失败, Err:" + err.Error()
+	//	return
+	//}
+	//if !pptMap.IsMoved && len(pptMapList) > 1 && count > 0 {
+	//	// 如果没有人为移动位置, 且当前用户有权限, 默认将当前ppt置顶
+	//	err = ppt.MoveGroupPpt(pptMap.GroupId, pptMap.GroupPptId, pptMapList[0].GroupPptId, 0, this.SysUser.AdminId)
+	//	if err != nil {
+	//		br.Msg = err.Error()
+	//		br.ErrMsg = "移动失败,Err:" + err.Error()
+	//		return
+	//	}
+	//}
 
 	//日志记录
 	logInfo := &models.PptV2SaveLog{
-		PptId:         int(req.PptId),
-		TemplateType:  req.FirstPage.TemplateType,
-		BackgroundImg: req.FirstPage.ImgUrl,
-		Title:         req.FirstPage.Title,
-		ReportType:    req.FirstPage.ReportType,
-		PptDate:       req.FirstPage.PptDate,
-		Content:       req.Content,
-		CoverContent:  req.CoverContent,
-		AdminId:       this.SysUser.AdminId,
-		AdminRealName: this.SysUser.RealName,
-		CreateTime:    time.Now(),
-		TitleSetting:  req.TitleSetting,
+		PptId:                int(req.PptId),
+		TemplateType:         req.FirstPage.TemplateType,
+		BackgroundImg:        req.FirstPage.ImgUrl,
+		CurrentBackgroundImg: req.FirstPage.CurrentBackgroundImg,
+		BackCoverImg:         req.FirstPage.BackCoverImg,
+		Title:                req.FirstPage.Title,
+		ReportType:           req.FirstPage.ReportType,
+		PptDate:              req.FirstPage.PptDate,
+		Content:              req.Content,
+		CoverContent:         req.CoverContent,
+		AdminId:              this.SysUser.AdminId,
+		AdminRealName:        this.SysUser.RealName,
+		CreateTime:           time.Now(),
+		TitleSetting:         req.TitleSetting,
 	}
 	_, e = models.AddPptV2SaveLog(logInfo)
 	if e != nil {

+ 300 - 0
controllers/ppt_v2_history.go

@@ -0,0 +1,300 @@
+package controllers
+
+import (
+	"encoding/json"
+	"eta_gn/eta_api/models"
+	"eta_gn/eta_api/utils"
+	"fmt"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"strings"
+	"time"
+)
+
+// PptV2HistoryController PPT
+type PptV2HistoryController struct {
+	BaseAuthController
+}
+
+// List
+// @Title 获取PPT列表接口
+// @Description 获取PPT列表
+// @Param   PptId   query   int  true       "PPTID"
+// @Param   PageSize   query   int  true       "每页数据条数"
+// @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
+// @Param   IsShowMe   query   bool  true       "是否只看我的,true、false"
+// @Success 200 {object} models.ReportListResp
+// @router /list [get]
+func (this *PptV2HistoryController) List() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	pageSize, _ := this.GetInt("PageSize")
+	currentIndex, _ := this.GetInt("CurrentIndex")
+	pptId, _ := this.GetInt("PptId")
+	isShowMe, _ := this.GetBool("IsShowMe")
+
+	if pptId <= 0 {
+		br.Msg = "请选择PPT"
+		br.ErrMsg = "请选择PPT"
+		return
+	}
+
+	var startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = utils.StartIndex(currentIndex, pageSize)
+
+	var condition string
+	var pars []interface{}
+
+	condition += ` AND ppt_id = ? `
+	pars = append(pars, pptId)
+
+	if isShowMe {
+		condition += ` AND admin_id = ? `
+		pars = append(pars, this.SysUser.AdminId)
+	}
+	var err error
+	var total int
+	var list []*models.PptV2HistoryListItem
+
+	historyObj := new(models.PptV2History)
+
+	total, err = historyObj.GetPageListCount(condition, pars)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+	listTmp, err := historyObj.GetNoContentPageList(condition, pars, startSize, pageSize)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+
+	for _, item := range listTmp {
+		tmp := &models.PptV2HistoryListItem{
+			Id:            item.Id,
+			PptId:         item.PptId,
+			TemplateType:  item.TemplateType,
+			BackgroundImg: item.BackgroundImg,
+			Title:         item.Title,
+			ReportType:    item.ReportType,
+			PptDate:       item.PptDate,
+			AdminId:       item.AdminId,
+			AdminRealName: item.AdminRealName,
+			CreateTime:    item.CreateTime.Format(utils.FormatDateTime),
+		}
+
+		list = append(list, tmp)
+	}
+
+	page := paging.GetPaging(currentIndex, pageSize, total)
+	resp := new(models.PptV2HistoryListResp)
+	resp.Paging = page
+	resp.List = list
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// Delete
+// @Title 删除版本
+// @Description 删除版本
+// @Param   Id   query   int  true       "版本ID"
+// @Success 200 {object} models.ReportListResp
+// @router /del [post]
+func (this *PptV2HistoryController) Delete() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	var req models.DeleteReportHistoryReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.Id <= 0 {
+		br.Msg = "请选择PPT版本"
+		return
+	}
+	historyObj := new(models.PptV2History)
+
+	item, err := historyObj.GetById(req.Id)
+	if err != nil {
+		br.Msg = "该版本已删除"
+		return
+	}
+
+	err = item.Delete()
+	if err != nil {
+		br.Msg = "删除失败"
+		br.ErrMsg = "删除失败,Err:" + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// Detail
+// @Title 获取PPT历史版本详情接口
+// @Description 获取PPT详情
+// @Param	request	body models.ReportDetailReq true "type json string"
+// @Success 200 {object} models.Report
+// @router /detail [get]
+func (this *PptV2HistoryController) Detail() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	id, _ := this.GetInt("Id")
+	if id <= 0 {
+		br.Msg = "请选择PPT版本"
+		return
+	}
+	historyObj := new(models.PptV2History)
+
+	history, err := historyObj.GetById(id)
+	if err != nil {
+		br.Msg = "该版本已删除"
+		return
+	}
+	pptId := history.PptId
+
+	pptInfo, err := models.GetPptV2ById(pptId)
+	if err != nil {
+		br.Msg = "信息获取失败"
+		br.ErrMsg = "信息获取失败,Err:" + err.Error()
+		return
+	}
+
+	pptInfo.TemplateType = history.TemplateType
+	pptInfo.BackgroundImg = history.BackgroundImg
+	pptInfo.Title = history.Title
+	pptInfo.ReportType = history.ReportType
+	pptInfo.PptDate = history.PptDate
+	pptInfo.Content = history.Content
+	pptInfo.CoverContent = history.CoverContent
+	pptInfo.TitleSetting = history.TitleSetting
+	pptInfo.AdminId = history.AdminId
+	pptInfo.AdminRealName = history.AdminRealName
+	pptInfo.ModifyTime = history.CreateTime
+
+	resp := new(models.PPTDetailResp)
+	resp.PptV2 = pptInfo
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// Revert
+// @Title 恢复PPT内容
+// @Description 恢复PPT内容
+// @Param   Id   query   int  true       "版本ID"
+// @Success 200 {object} models.ReportListResp
+// @router /revert [post]
+func (this *PptV2HistoryController) Revert() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req models.DeleteReportHistoryReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.Id <= 0 {
+		br.Msg = "请选择PPT版本"
+		return
+	}
+	historyObj := new(models.PptV2History)
+
+	history, err := historyObj.GetById(req.Id)
+	if err != nil {
+		br.Msg = "该版本已删除"
+		return
+	}
+	// 获取PPT详情
+	pptInfo, err := models.GetPptV2ById(history.PptId)
+	if err != nil {
+		br.Msg = "信息获取失败"
+		br.ErrMsg = "信息获取失败,Err:" + err.Error()
+		return
+	}
+
+	// 协作人权限
+	if pptInfo.AdminId != this.SysUser.AdminId {
+		if pptInfo.CollaborateType != utils.ReportWriteTypeGroup {
+			br.Msg = "非协作人无权操作"
+			return
+		}
+		if pptInfo.CollaborateUsers == "" {
+			br.Msg = "非协作人无权操作"
+			return
+		}
+		partnerArr := strings.Split(pptInfo.CollaborateUsers, ",")
+		if !utils.InArrayByStr(partnerArr, fmt.Sprint(this.SysUser.AdminId)) {
+			br.Msg = "非协作人无权操作"
+			return
+		}
+	}
+
+	// 修改
+	pptInfo.TemplateType = history.TemplateType
+	pptInfo.BackgroundImg = history.BackgroundImg
+	pptInfo.Title = history.Title
+	pptInfo.ReportType = history.ReportType
+	pptInfo.PptDate = history.PptDate
+	pptInfo.Content = history.Content
+	pptInfo.CoverContent = history.CoverContent
+	pptInfo.ModifyTime = time.Now()
+	pptInfo.TitleSetting = history.TitleSetting
+	err = pptInfo.Update([]string{"TemplateType", "BackgroundImg", "Title", "ReportType", "PptDate", "Content", "ModifyTime", "CoverContent", "TitleSetting"})
+	if err != nil {
+		br.Msg = "编辑失败"
+		br.ErrMsg = "编辑失败,Err:" + err.Error()
+		return
+	}
+
+	// 记录修改日志
+	{
+		logInfo := &models.PptV2SaveLog{
+			PptId:         pptInfo.PptId,
+			TemplateType:  pptInfo.TemplateType,
+			BackgroundImg: pptInfo.BackgroundImg,
+			Title:         pptInfo.Title,
+			ReportType:    pptInfo.ReportType,
+			PptDate:       pptInfo.PptDate,
+			Content:       pptInfo.Content,
+			CoverContent:  pptInfo.CoverContent,
+			AdminId:       this.SysUser.AdminId,
+			AdminRealName: this.SysUser.RealName,
+			CreateTime:    time.Now(),
+		}
+		_, err = models.AddPptV2SaveLog(logInfo)
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}

+ 22 - 1
controllers/report_chapter.go

@@ -470,7 +470,28 @@ func (this *ReportController) EditDayWeekChapter() {
 			return
 		}
 	}
-
+	// 增加版本记录
+	if req.IsManualSave {
+		reportHistory := new(models.ReportHistory)
+		reportHistory.ReportId = reportChapterInfo.ReportId
+		reportHistory.ReportChapterId = reportChapterInfo.ReportChapterId
+		reportHistory.Content = reportChapterInfo.Content
+		reportHistory.Title = reportChapterInfo.Title
+		reportHistory.ContentSub = reportChapterInfo.ContentSub
+		reportHistory.ContentStruct = reportChapterInfo.ContentStruct
+		reportHistory.CanvasColor = req.CanvasColor
+		reportHistory.HeadResourceId = req.HeadResourceId
+		reportHistory.EndResourceId = req.EndResourceId
+		reportHistory.AdminId = sysUser.AdminId
+		reportHistory.AdminName = sysUser.AdminName
+		reportHistory.CreateTime = time.Now()
+		err = reportHistory.Add()
+		if err != nil {
+			br.Msg = "保存失败"
+			br.ErrMsg = "保存失败,Err:" + err.Error()
+			return
+		}
+	}
 	// 备份关键数据
 	chapters := make([]*models.ReportChapter, 0)
 	chapters = append(chapters, reportChapterInfo)

+ 443 - 0
controllers/report_history.go

@@ -0,0 +1,443 @@
+package controllers
+
+import (
+	"encoding/json"
+	"eta_gn/eta_api/models"
+	"eta_gn/eta_api/models/report"
+	"eta_gn/eta_api/models/smart_report"
+	"eta_gn/eta_api/services"
+	"eta_gn/eta_api/utils"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"html"
+	"time"
+)
+
+// ReportHistoryController 报告
+type ReportHistoryController struct {
+	BaseAuthController
+}
+
+// List
+// @Title 获取报告列表接口
+// @Description 获取报告列表
+// @Param   ReportId   query   int  true       "报告ID"
+// @Param   ReportChapterId   query   int  true       "报告ID"
+// @Param   PageSize   query   int  true       "每页数据条数"
+// @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
+// @Param   IsShowMe   query   bool  true       "是否只看我的,true、false"
+// @Success 200 {object} models.ReportListResp
+// @router /list [get]
+func (this *ReportHistoryController) List() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	pageSize, _ := this.GetInt("PageSize")
+	currentIndex, _ := this.GetInt("CurrentIndex")
+	reportId, _ := this.GetInt("ReportId")
+	reportChapterId, _ := this.GetInt("ReportChapterId")
+	isShowMe, _ := this.GetBool("IsShowMe")
+
+	if reportId <= 0 && reportChapterId <= 0 {
+		br.Msg = "请选择报告"
+		br.ErrMsg = "请选择报告"
+		return
+	}
+
+	var startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = utils.StartIndex(currentIndex, pageSize)
+
+	var condition string
+	var pars []interface{}
+	if reportId > 0 {
+		condition += ` AND report_id = ? `
+		pars = append(pars, reportId)
+	}
+	if reportChapterId > 0 {
+		condition += ` AND report_chapter_id = ? `
+		pars = append(pars, reportChapterId)
+	}
+	if isShowMe {
+		condition += ` AND admin_id = ? `
+		pars = append(pars, this.SysUser.AdminId)
+	}
+	var err error
+	var total int
+	var list []*models.ReportHistoryListItem
+
+	historyObj := new(models.ReportHistory)
+
+	total, err = historyObj.GetPageListCount(condition, pars)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+	listTmp, err := historyObj.GetNoContentPageList(condition, pars, startSize, pageSize)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+
+	for _, item := range listTmp {
+		tmp := new(models.ReportHistoryListItem)
+		tmp.Id = item.Id
+		tmp.ReportId = item.ReportId
+		tmp.ReportChapterId = item.ReportChapterId
+		tmp.CreateTime = item.CreateTime.Format(utils.FormatDateTime)
+		tmp.AdminId = item.AdminId
+		tmp.AdminName = item.AdminName
+		tmp.Title = item.Title
+		list = append(list, tmp)
+	}
+
+	page := paging.GetPaging(currentIndex, pageSize, total)
+	resp := new(models.ReportHistoryListResp)
+	resp.Paging = page
+	resp.List = list
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// Delete
+// @Title 删除版本
+// @Description 删除版本
+// @Param   Id   query   int  true       "版本ID"
+// @Success 200 {object} models.ReportListResp
+// @router /del [post]
+func (this *ReportHistoryController) Delete() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	var req models.DeleteReportHistoryReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.Id <= 0 {
+		br.Msg = "请选择报告版本"
+		return
+	}
+	historyObj := new(models.ReportHistory)
+
+	item, err := historyObj.GetById(req.Id)
+	if err != nil {
+		br.Msg = "该版本已删除"
+		return
+	}
+
+	err = item.Delete()
+	if err != nil {
+		br.Msg = "删除失败"
+		br.ErrMsg = "删除失败,Err:" + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// Detail
+// @Title 获取报告历史版本详情接口
+// @Description 获取报告详情
+// @Param	request	body models.ReportDetailReq true "type json string"
+// @Success 200 {object} models.Report
+// @router /detail [get]
+func (this *ReportHistoryController) Detail() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	id, _ := this.GetInt("Id")
+	if id <= 0 {
+		br.Msg = "请选择报告版本"
+		return
+	}
+	historyObj := new(models.ReportHistory)
+
+	history, err := historyObj.GetById(id)
+	if err != nil {
+		br.Msg = "该版本已删除"
+		return
+	}
+	reportId := history.ReportId
+	reportChapterId := history.ReportChapterId
+
+	reportInfo, err := models.GetReportById(reportId)
+	if err != nil {
+		if utils.IsErrNoRow(err) {
+			br.Msg = "报告已被删除"
+			return
+		}
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+
+	chapterList := make([]*models.ReportChapter, 0)
+	if reportInfo.HasChapter == 1 && reportChapterId > 0 {
+		chapter, e := models.GetReportChapterInfoById(reportChapterId)
+		if e != nil {
+			if e.Error() == utils.ErrNoRow() {
+				br.Msg = "章节已删除"
+				return
+			}
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取失败,Err:" + e.Error()
+			return
+		}
+		chapter.Content = html.UnescapeString(history.Content)
+		chapter.ContentSub = html.UnescapeString(history.ContentSub)
+		chapter.ContentStruct = html.UnescapeString(history.ContentStruct)
+		chapter.LastModifyAdminName = history.AdminName
+		chapter.ContentModifyTime = history.CreateTime
+		chapter.LastModifyAdminId = history.AdminId
+		chapterList = append(chapterList, chapter)
+	} else {
+		reportInfo.Title = history.Title
+		reportInfo.Content = html.UnescapeString(history.Content)
+		reportInfo.ContentSub = html.UnescapeString(history.ContentSub)
+		reportInfo.ContentStruct = html.UnescapeString(history.ContentStruct)
+		reportInfo.CanvasColor = history.CanvasColor
+		reportInfo.LastModifyAdminName = history.AdminName
+		reportInfo.LastModifyAdminId = history.AdminId
+		reportInfo.ContentModifyTime = history.CreateTime.Format(utils.FormatDate)
+	}
+
+	if history.HeadResourceId > 0 {
+		headResource, err := smart_report.GetResourceItemById(history.HeadResourceId)
+		if err != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = "获取资源库版头失败, Err: " + err.Error()
+			return
+		}
+		reportInfo.HeadImg = headResource.ImgUrl
+		reportInfo.HeadStyle = headResource.Style
+	} else {
+		reportInfo.HeadImg = ""
+		reportInfo.HeadStyle = ""
+	}
+
+	if history.EndResourceId > 0 {
+		endResource, err := smart_report.GetResourceItemById(history.EndResourceId)
+		if err != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = "获取资源库版头失败, Err: " + err.Error()
+			return
+		}
+		reportInfo.EndImg = endResource.ImgUrl
+		reportInfo.EndStyle = endResource.Style
+	} else {
+		reportInfo.EndImg = ""
+		reportInfo.EndStyle = ""
+	}
+
+	resp := &models.ReportDetailView{
+		ReportDetail: reportInfo,
+		ChapterList:  chapterList,
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// Revert
+// @Title 恢复报告内容
+// @Description 恢复报告内容
+// @Param   Id   query   int  true       "版本ID"
+// @Success 200 {object} models.ReportListResp
+// @router /revert [post]
+func (this *ReportHistoryController) Revert() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req models.DeleteReportHistoryReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.Id <= 0 {
+		br.Msg = "请选择报告版本"
+		return
+	}
+	historyObj := new(models.ReportHistory)
+
+	history, err := historyObj.GetById(req.Id)
+	if err != nil {
+		br.Msg = "该版本已删除"
+		return
+	}
+	// 获取报告详情
+	reportInfo, err := models.GetReportByReportId(history.ReportId)
+	if err != nil {
+		if utils.IsErrNoRow(err) {
+			br.Msg = "报告已删除"
+			return
+		}
+		br.Msg = "获取报告失败"
+		br.ErrMsg = "获取报告失败,Err:" + err.Error()
+		return
+	}
+	if reportInfo.Id > 0 && reportInfo.State == 2 {
+		br.Msg = "该报告已发布,不允许编辑"
+		br.ErrMsg = "该报告已发布,不允许编辑"
+		br.IsSendEmail = false
+		return
+	}
+
+	// 恢复章节
+	if history.ReportChapterId > 0 {
+		// 如果不是创建人,那么就要去查看是否授权
+		reportChapterInfo, e := models.GetReportChapterInfoById(history.ReportChapterId)
+		if e != nil {
+			if utils.IsErrNoRow(e) {
+				br.Msg = "章节已删除"
+				return
+			}
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取失败,Err:" + e.Error()
+			return
+		}
+		if reportInfo.AdminId != this.SysUser.AdminId {
+			// 授权用户权限校验
+			chapterGrantObj := report.ReportChapterGrant{}
+			_, tmpErr := chapterGrantObj.GetGrantByIdAndAdmin(reportChapterInfo.ReportChapterId, this.SysUser.AdminId)
+			if tmpErr != nil {
+				if utils.IsErrNoRow(tmpErr) {
+					br.Msg = "没有权限"
+					br.ErrMsg = "没有权限"
+					br.IsSendEmail = false
+					return
+				}
+				br.Msg = "获取章节id授权用户失败"
+				br.ErrMsg = "获取章节id授权用户失败, Err: " + tmpErr.Error()
+				return
+			}
+		}
+
+		// 标记更新中
+		{
+			markStatus, err := services.UpdateReportEditMark(reportChapterInfo.ReportId, reportChapterInfo.ReportChapterId, this.SysUser.AdminId, 1, this.SysUser.RealName, this.Lang)
+			if err != nil {
+				br.Msg = err.Error()
+				return
+			}
+			if markStatus.Status == 1 {
+				br.Msg = markStatus.Msg
+				br.IsSendEmail = false
+				return
+			}
+		}
+		reportChapterInfo.Title = history.Title
+		reportChapterInfo.Content = history.Content
+		reportChapterInfo.ContentSub = history.ContentSub
+		reportChapterInfo.IsEdit = 1
+		reportChapterInfo.ModifyTime = time.Now()
+		reportChapterInfo.LastModifyAdminId = this.SysUser.AdminId
+		reportChapterInfo.LastModifyAdminName = this.SysUser.RealName
+		reportChapterInfo.ContentModifyTime = time.Now()
+		reportChapterInfo.ContentStruct = history.ContentStruct
+		updateCols := make([]string, 0)
+		updateCols = append(updateCols, "Title", "Content", "ContentSub", "IsEdit", "ModifyTime")
+		updateCols = append(updateCols, "LastModifyAdminId", "LastModifyAdminName", "ContentModifyTime", "ContentStruct")
+		err = reportChapterInfo.Update(updateCols)
+		if err != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = "操作失败,Err:" + err.Error()
+			return
+		}
+	} else {
+		// 标记更新中
+		{
+			markStatus, err := services.UpdateReportEditMark(history.ReportId, 0, this.SysUser.AdminId, 1, this.SysUser.RealName, this.Lang)
+			if err != nil {
+				br.Msg = err.Error()
+				return
+			}
+			if markStatus.Status == 1 {
+				br.Msg = markStatus.Msg
+				return
+			}
+		}
+
+		if history.HeadResourceId > 0 {
+			headResource, err := smart_report.GetResourceItemById(history.HeadResourceId)
+			if err != nil {
+				br.Msg = "操作失败"
+				br.ErrMsg = "获取资源库版头失败, Err: " + err.Error()
+				return
+			}
+			reportInfo.HeadImg = headResource.ImgUrl
+		} else {
+			reportInfo.HeadImg = ""
+		}
+
+		if history.EndResourceId > 0 {
+			endResource, err := smart_report.GetResourceItemById(history.EndResourceId)
+			if err != nil {
+				br.Msg = "操作失败"
+				br.ErrMsg = "获取资源库版头失败, Err: " + err.Error()
+				return
+			}
+			reportInfo.EndImg = endResource.ImgUrl
+		} else {
+			reportInfo.EndImg = ""
+		}
+		// 恢复报告
+		// todo 标题是否需要恢复
+		reportInfo.Title = history.Title
+		reportInfo.Content = history.Content
+		reportInfo.ContentSub = history.ContentSub
+		reportInfo.ContentStruct = history.ContentStruct
+		reportInfo.CanvasColor = history.CanvasColor
+		reportInfo.HeadResourceId = history.HeadResourceId
+		reportInfo.EndResourceId = history.EndResourceId
+		reportInfo.ModifyTime = time.Now()
+		reportInfo.ContentModifyTime = time.Now()
+		updateCols := []string{"Title", "Content", "ContentSub", "ContentStruct", "HeadImg", "EndImg", "CanvasColor", "HeadResourceId", "EndResourceId", "ModifyTime", "ContentModifyTime"}
+		err = reportInfo.UpdateReport(updateCols)
+		if err != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = "操作失败,Err:" + err.Error()
+			return
+		}
+	}
+
+	// 报告的最后编辑人
+	reportInfo.LastModifyAdminId = this.SysUser.AdminId
+	reportInfo.LastModifyAdminName = this.SysUser.RealName
+	reportInfo.ModifyTime = time.Now()
+	err = reportInfo.UpdateReport([]string{"LastModifyAdminId", "LastModifyAdminName", "ModifyTime"})
+	if err != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "操作失败,Err:" + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}

+ 251 - 23
controllers/report_v2.go

@@ -12,13 +12,12 @@ import (
 	"eta_gn/eta_api/services/data"
 	"eta_gn/eta_api/utils"
 	"fmt"
-	"github.com/rdlucklib/rdluck_tools/paging"
 	"html"
-	"io"
-	"os"
 	"strconv"
 	"strings"
 	"time"
+
+	"github.com/rdlucklib/rdluck_tools/paging"
 )
 
 // ListReport
@@ -80,7 +79,6 @@ func (this *ReportController) ListReport() {
 
 	var condition string
 	var pars []interface{}
-
 	if keyWord != "" {
 		condition += ` AND (a.title LIKE ? OR a.admin_real_name LIKE ? ) `
 		pars = utils.GetLikeKeywordPars(pars, keyWord, 2)
@@ -141,6 +139,17 @@ func (this *ReportController) ListReport() {
 		pars = append(pars, this.SysUser.AdminId, this.SysUser.AdminId)
 	}
 
+	noVisibleClassifyIds, err := models.GetNoVisibleClassifyIdByAdminId(this.SysUser.AdminId)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取不可见分类id失败,Err:" + err.Error()
+		return
+	}
+	if len(noVisibleClassifyIds) > 0 {
+		condition += ` AND ( CASE WHEN a.classify_id_third > 0 THEN a.classify_id_third WHEN a.classify_id_second > 0 THEN a.classify_id_second ELSE a.classify_id_first END) NOT IN (?) `
+		pars = append(pars, noVisibleClassifyIds)
+	}
+
 	// 共享报告需要连表查询,所以需要单独写
 	if filterReportType == 2 {
 		total, err = models.GetReportListCountByGrant(condition, pars)
@@ -427,9 +436,10 @@ func (this *ReportController) Add() {
 		req.ReportLayout = 1
 	}
 	// 是否公开发布,1:是,2:否
-	if req.IsPublicPublish == 0 {
-		req.IsPublicPublish = 1
-	}
+	req.IsPublicPublish = 2 // 本地报告固定不公开
+	//if req.IsPublicPublish == 0 {
+	//	req.IsPublicPublish = 1
+	//}
 
 	classifyItemList, err := models.GetClassifyListByIdList([]int{req.ClassifyIdFirst, req.ClassifyIdSecond, req.ClassifyIdThird})
 	classifyMap := make(map[int]string)
@@ -492,6 +502,7 @@ func (this *ReportController) Add() {
 	item.ReportLayout = req.ReportLayout
 	item.IsPublicPublish = req.IsPublicPublish
 	item.ReportCreateTime = time.Now()
+	item.ReportSource = utils.ReportSourceLocal
 
 	err, errMsg := services.AddReportAndChapter(item, req.InheritReportId, req.GrantAdminIdList)
 	if err != nil {
@@ -747,8 +758,18 @@ func (this *ReportController) SaveReportContent() {
 	}
 
 	// 获取报告详情
-	reportInfo, _ := models.GetReportByReportId(req.ReportId)
-	if reportInfo != nil && reportInfo.State == 2 {
+	reportInfo, err := models.GetReportByReportId(req.ReportId)
+	if err != nil {
+		if utils.IsErrNoRow(err) {
+			br.Msg = "该报告不存在"
+			br.ErrMsg = "该报告不存在, Err: " + err.Error()
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取报告详情失败, Err: " + err.Error()
+		return
+	}
+	if reportInfo.Id > 0 && reportInfo.State == 2 {
 		br.Msg = "该报告已发布,不允许编辑"
 		br.ErrMsg = "该报告已发布,不允许编辑"
 		br.IsSendEmail = false
@@ -769,7 +790,7 @@ func (this *ReportController) SaveReportContent() {
 	}
 
 	// 内容有过修改的话,那么逻辑处理
-	if noChangeFlag != 1 {
+	if noChangeFlag != 1 || req.IsManualSave {
 		content := req.Content
 		if content == "" {
 			content = this.GetString("Content")
@@ -811,6 +832,27 @@ func (this *ReportController) SaveReportContent() {
 				br.ErrMsg = "保存失败,Err:" + err.Error()
 				return
 			}
+			// 增加版本记录
+			if req.IsManualSave {
+				reportHistory := new(models.ReportHistory)
+				reportHistory.ReportId = req.ReportId
+				reportHistory.Title = reportInfo.Title
+				reportHistory.Content = reportInfo.Content
+				reportHistory.ContentSub = reportInfo.ContentSub
+				reportHistory.ContentStruct = reportInfo.ContentStruct
+				reportHistory.CanvasColor = reportInfo.CanvasColor
+				reportHistory.HeadResourceId = reportInfo.HeadResourceId
+				reportHistory.EndResourceId = reportInfo.EndResourceId
+				reportHistory.AdminId = sysUser.AdminId
+				reportHistory.AdminName = sysUser.AdminName
+				reportHistory.CreateTime = time.Now()
+				e = reportHistory.Add()
+				if e != nil {
+					br.Msg = "保存失败"
+					br.ErrMsg = "保存失败,Err:" + e.Error()
+					return
+				}
+			}
 			go models.AddReportSaveLog(reportId, this.SysUser.AdminId, reportInfo.Content, reportInfo.ContentSub, reportInfo.ContentStruct, reportInfo.CanvasColor, this.SysUser.AdminName, reportInfo.HeadResourceId, reportInfo.EndResourceId)
 		}
 	}
@@ -908,6 +950,16 @@ func (this *ReportController) AuthorizedListReport() {
 
 	var err error
 	var total int
+	noVisibleClassifyIds, err := models.GetNoVisibleClassifyIdByAdminId(this.SysUser.AdminId)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取不可见分类id失败,Err:" + err.Error()
+		return
+	}
+	if len(noVisibleClassifyIds) > 0 {
+		condition += ` AND ( CASE WHEN a.classify_id_third > 0 THEN a.classify_id_third WHEN a.classify_id_second > 0 THEN a.classify_id_second ELSE a.classify_id_first END) NOT IN (?) `
+		pars = append(pars, noVisibleClassifyIds)
+	}
 
 	orCondition := `AND ( (a.is_public_publish = ? AND a.state in (2,6)) or a.admin_id = ? `
 	pars = append(pars, 1, this.SysUser.AdminId)
@@ -1233,7 +1285,7 @@ func (this *ReportController) PublishReport() {
 		}
 
 		// 报告发布
-		tmpTips, err, errMsg := services.PublishReport(vint, req.ReportUrl, this.SysUser)
+		tmpTips, err, errMsg := services.PublishReportV2(vint, this.SysUser)
 		if err != nil {
 			br.Msg = errMsg
 			br.ErrMsg = "报告发布失败,Err:" + err.Error()
@@ -1663,24 +1715,200 @@ func (this *ReportController) CancelApprove() {
 	br.Msg = "操作成功"
 }
 
-func initPdf() {
-	inFile := "anNNgk3Bbi4LRULwcJgNOPrREYh5.pdf"
-	f2, err := services.GeneralWaterMarkPdf(inFile, "颜鹏 - 18170239278")
-	//f2, err := services.GeneralWaterMarkPdf(inFile, "上周美国馏分油库存累库95万桶,馏分油表需环比下降(-25.6万桶/日)。本期馏分油产量继续抬升,在供增需减的环比变动下库存持续累库。馏分油供应的增加我们认为可能和进口的油种有关,今年以来美国进口的中重质原油占比不断走高,尤其是5")
-	if err != nil {
-		fmt.Println("生成失败,ERR:", err)
+// MessageList
+// @Title 报告消息列表
+// @Description 报告消息列表
+// @Param   PageSize			query	int		true	"每页数据条数"
+// @Param   CurrentIndex		query	int		true	"当前页页码"
+// @Success 200 {object} models.ReportMessageListResp
+// @router /message/list [get]
+func (this *ReportController) 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
 	}
+	pageSize, _ := this.GetInt("PageSize")
+	pageIndex, _ := this.GetInt("CurrentIndex")
 
-	// 创建一个新的文件
-	newPdf, err := os.Create("new0555.pdf")
-	if err != nil {
-		fmt.Println("创建临时文件失败,Err:", err)
+	// 分页
+	var startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if pageIndex <= 0 {
+		pageIndex = 1
+	}
+	startSize = utils.StartIndex(pageIndex, pageSize)
+
+	resp := new(models.ReportMessageListResp)
+	resp.List = make([]*models.ReportMessageItem, 0)
+	cond := fmt.Sprintf(` AND %s = ?`, models.ReportMessageCols.ReceiveUserId)
+	pars := make([]interface{}, 0)
+	pars = append(pars, sysUser.AdminId)
+	order := fmt.Sprintf(`%s ASC, %s DESC`, models.ReportMessageCols.IsRead, models.ReportMessageCols.CreateTime)
+
+	messageOb := new(models.ReportMessage)
+	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, pageSize)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取消息列表失败, Err: " + e.Error()
 		return
 	}
+	var reportIds, pptIds []int
+	for _, v := range list {
+		t := models.FormatReportMessage2Item(v)
+		if v.ExtraContent != "" {
+			am := new(models.ReportMessageApproveItem)
+			if e = json.Unmarshal([]byte(v.ExtraContent), &am); e != nil {
+				br.Msg = "获取失败"
+				br.ErrMsg = fmt.Sprintf("消息列表审批信息解析失败, %v", e)
+				return
+			}
+			t.ApproveMsg = am
+		}
+		resp.List = append(resp.List, t)
+
+		// 报告状态,这里动态去获取
+		if v.ReportType == utils.ReportTypeDefault {
+			reportIds = append(reportIds, v.ReportId)
+		}
+		if v.ReportType == utils.ReportTypePPT {
+			pptIds = append(pptIds, v.ReportId)
+		}
+	}
+
+	stateReport := make(map[int]int)
+	statePpt := make(map[int]int)
+	if len(reportIds) > 0 {
+		ob := new(models.Report)
+		cond := ` AND id IN (?)`
+		pars := make([]interface{}, 0)
+		pars = append(pars, reportIds)
+		items, e := ob.GetItemsByCondition(cond, pars, []string{"id", "state"}, "")
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("获取报告列表失败, %v", e)
+			return
+		}
+		for _, v := range items {
+			stateReport[v.Id] = v.State
+		}
+	}
+	if len(pptIds) > 0 {
+		ob := new(models.PptV2)
+		cond := ` AND ppt_id IN (?)`
+		pars := make([]interface{}, 0)
+		pars = append(pars, pptIds)
+		items, e := ob.GetItemsByCondition(cond, pars, []string{"ppt_id", "state"}, "")
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("获取PPT列表失败, %v", e)
+			return
+		}
+		for _, v := range items {
+			statePpt[v.PptId] = v.State
+		}
+	}
+	for _, v := range resp.List {
+		if v.ReportType == utils.ReportTypeDefault {
+			v.ReportState = stateReport[v.ReportId]
+		}
+		if v.ReportType == utils.ReportTypePPT {
+			v.ReportState = statePpt[v.ReportId]
+		}
+	}
+
+	// 未读消息数
+	cond += fmt.Sprintf(` AND %s = ?`, models.ReportMessageCols.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(pageIndex, pageSize, total)
+	resp.Paging = page
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// MessageRead
+// @Title 消息已读
+// @Description 消息已读
+// @Param	request	body models.ReportMessageReadReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /message/read [post]
+func (this *ReportController) MessageRead() {
+	br := new(models.BaseResponse).Init()
 	defer func() {
-		_ = newPdf.Close()
+		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 models.ReportMessageReadReq
+	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
+	}
 
-	_, _ = io.Copy(newPdf, f2)
+	messageOb := new(models.ReportMessage)
+	messageItem, e := messageOb.GetItemById(req.MessageId)
+	if e != nil {
+		if utils.IsErrNoRow(e) {
+			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 = "操作成功"
 }

+ 35 - 20
controllers/sandbox/sandbox.go

@@ -2395,16 +2395,21 @@ func (this *SandboxController) LinkEdbInfoCheck() {
 		return
 	}
 	edbList := make([]*sandbox.SandboxLinkCheckItem, 0)
-	for _, v := range edbInfoList {
-		tmp := &sandbox.SandboxLinkCheckItem{
-			Id:         v.EdbInfoId,
-			Name:       v.EdbName,
-			UniqueCode: v.UniqueCode,
-			ClassifyId: v.ClassifyId,
+	for _, id := range req.EdbInfoIdList {
+		for _, v := range edbInfoList {
+			if v.EdbInfoId == id {
+				tmp := &sandbox.SandboxLinkCheckItem{
+					Id:         v.EdbInfoId,
+					Name:       v.EdbName,
+					UniqueCode: v.UniqueCode,
+					ClassifyId: v.ClassifyId,
+				}
+				edbList = append(edbList, tmp)
+			}
 		}
-		edbList = append(edbList, tmp)
 	}
 
+
 	chartList, err := data_manage.GetChartInfoByIdList(req.ChartInfoIdList)
 	if err != nil {
 		br.Msg = `获取失败`
@@ -2412,16 +2417,21 @@ func (this *SandboxController) LinkEdbInfoCheck() {
 		return
 	}
 	chartListTmp := make([]*sandbox.SandboxLinkCheckItem, 0)
-	for _, v := range chartList {
-		tmp := &sandbox.SandboxLinkCheckItem{
-			Id:         v.ChartInfoId,
-			Name:       v.ChartName,
-			UniqueCode: v.UniqueCode,
-			ClassifyId: v.ChartClassifyId,
+	for _, id := range req.ChartInfoIdList {
+		for _, v := range chartList {
+			if v.ChartInfoId == id {
+				tmp := &sandbox.SandboxLinkCheckItem{
+					Id:         v.ChartInfoId,
+					Name:       v.ChartName,
+					UniqueCode: v.UniqueCode,
+					ClassifyId: v.ChartClassifyId,
+				}
+				chartListTmp = append(chartListTmp, tmp)
+			}
 		}
-		chartListTmp = append(chartListTmp, tmp)
 	}
 
+
 	reportList, err := models.GetSimpleReportByIds(req.ReportIdList)
 	if err != nil {
 		br.Msg = `获取失败`
@@ -2429,14 +2439,19 @@ func (this *SandboxController) LinkEdbInfoCheck() {
 		return
 	}
 	reportListTmp := make([]*sandbox.SandboxLinkCheckItem, 0)
-	for _, v := range reportList {
-		tmp := &sandbox.SandboxLinkCheckItem{
-			Id:         v.Id,
-			Name:       v.Title,
-			UniqueCode: v.ReportCode,
+	for _, id := range req.ReportIdList {
+		for _, v := range reportList {
+			if v.Id == id {
+				tmp := &sandbox.SandboxLinkCheckItem{
+					Id:         v.Id,
+					Name:       v.Title,
+					UniqueCode: v.ReportCode,
+				}
+				reportListTmp = append(reportListTmp, tmp)
+			}
 		}
-		reportListTmp = append(reportListTmp, tmp)
 	}
+
 	resp.EdbInfoIdList = edbList
 	resp.ChartInfoIdList = chartListTmp
 	resp.ReportIdList = reportListTmp

+ 1 - 0
models/business_conf.go

@@ -55,6 +55,7 @@ const (
 	BusinessConfSmsJhgjVariable              = "SmsJhgjVariable"              // 聚合国际短信变量
 
 	BusinessConfEdbStopRefreshRule = "EdbStopRefreshRule" // 是否停止指标刷新规则
+	BusinessConfOuterReportApiUrl  = "OuterReportApiUrl"  // 智力共享-报告API地址
 )
 
 const (

+ 91 - 14
models/classify.go

@@ -4,8 +4,10 @@ import (
 	"eta_gn/eta_api/global"
 	"eta_gn/eta_api/utils"
 	"fmt"
-	"github.com/rdlucklib/rdluck_tools/paging"
+	"strings"
 	"time"
+
+	"github.com/rdlucklib/rdluck_tools/paging"
 )
 
 type Classify struct {
@@ -44,12 +46,21 @@ type Classify struct {
 	Level                int       `gorm:"column:level" json:"level"`                                     //`description:"层级"`
 	HasChild             int       `gorm:"column:has_child" json:"has_child"`                             //`description:"是否有子级别,0:下面没有子分类,1:下面有子分类;默认:0"`
 	ReportDetailShowType int       `gorm:"column:report_detail_show_type" json:"report_detail_show_type"` //`description:"报告详情的展示类型:1-拼接;2:目录"`
+	ClassifyType         int       `gorm:"column:classify_type" json:"classify_type"`                     //`description:"分类类型:1-研报;2-PPT"`
+	IsRemind             int       `gorm:"column:is_remind" json:"is_remind"`                             //`description:"是否开启提醒:0-关闭;1-开启"`
+	RemindTime           string    `gorm:"column:remind_time" json:"remind_time"`                         //`description:"提醒时间:可选00:00-23:59"`
+	ReportNum            int       `gorm:"column:report_num" json:"report_num"`                           //`description:"分类下的报告数"`
+	LevelPath            string    `gorm:"column:level_path" json:"level_path"`                           //`description:"分类的层级路径,英文逗号分隔"`
 }
 
 type ClassifyAddReq struct {
 	ClassifyName          string `description:"分类名称"`
 	ParentId              int    `description:"父级分类id,没有父级分类传0"`
 	ChartPermissionIdList []int  `description:"权限id数组"`
+	VisibleUserIds        []int  `description:"可见用户id数组"`
+	ClassifyType          int    `description:"分类类型:1-研报;2-PPT"`
+	IsRemind              int    `description:"是否开启提醒:0-关闭;1-开启"`
+	RemindTime            string `description:"提醒时间:可选00:00-23:59"`
 }
 
 func GetClassifyByName(classifyName string, parentId int) (item *Classify, err error) {
@@ -65,8 +76,44 @@ func GetClassifyById(classifyId int) (item *Classify, err error) {
 }
 
 // 添加分类
-func AddClassify(item *Classify) (err error) {
-	err = global.DmSQL["rddp"].Create(item).Error
+func AddClassify(item *Classify, parentId int, visibleUserIds []int) (err error) {
+	tx := global.DmSQL["rddp"].Begin()
+	defer func() {
+		if err != nil {
+			tx.Rollback()
+			return
+		}
+		tx.Commit()
+	}()
+	err = tx.Create(item).Error
+	if err != nil {
+		return
+	}
+	// 添加可见用户
+	if len(visibleUserIds) > 0 {
+		addMappingList := make([]ClassifyVisible, 0)
+		now := time.Now()
+		for _, visibleUserId := range visibleUserIds {
+			addMapping := ClassifyVisible{
+				ClassifyId: item.Id,
+				AdminId:    visibleUserId,
+				CreateTime: now,
+			}
+			addMappingList = append(addMappingList, addMapping)
+		}
+		err = tx.CreateInBatches(addMappingList, utils.MultiAddNum).Error
+		if err != nil {
+			return
+		}
+	}
+	// 删除父级分类可见权限
+	if parentId > 0 {
+		sql := `DELETE FROM classify_visible WHERE classify_id=? `
+		err = tx.Exec(sql, parentId).Error
+		if err != nil {
+			return
+		}
+	}
 	return
 }
 
@@ -93,15 +140,18 @@ func GetClassifySubCountByParentId(classifyId int) (count int, err error) {
 	return
 }
 
-// 删除分类
-func DeleteClassify(classifyId int) (err error) {
-	sql := `DELETE FROM classify WHERE id=? `
-	err = global.DmSQL["rddp"].Exec(sql, classifyId).Error
+// DeleteClassify 删除分类
+func DeleteClassify(classifyIds []int) (err error) {
+	if len(classifyIds) == 0 {
+		return
+	}
+	sql := fmt.Sprintf(`DELETE FROM classify WHERE id IN (%s)`, utils.GetOrmInReplace(len(classifyIds)))
+	err = global.DmSQL["rddp"].Exec(sql, classifyIds).Error
 	if err != nil {
 		return
 	}
-	deleteImgSql := `DELETE FROM banner WHERE classify_id=? `
-	err = global.DmSQL["rddp"].Exec(deleteImgSql, classifyId).Error
+	deleteImgSql := fmt.Sprintf(`DELETE FROM banner WHERE classify_id IN (%s)`, utils.GetOrmInReplace(len(classifyIds)))
+	err = global.DmSQL["rddp"].Exec(deleteImgSql, classifyIds).Error
 	return
 }
 
@@ -126,7 +176,7 @@ type ClassifyList struct {
 	HasTeleconference     int             `gorm:"column:has_teleconference"` //`description:"是否有电话会:0-否 1-是"`
 	IsShow                int             `gorm:"column:is_show"`            //`description:"是否在小程序显示:1-显示 0-隐藏"`
 	YbFiccSort            int             `gorm:"column:yb_ficc_sort"`       //`description:"小程序FICC页排序"`
-	YbFiccIcon            string          `gorm:"column:yb_ficc_icon"`       // `description:"小程序FICC页icon"`
+	YbFiccIcon            string          `gorm:"column:yb_ficc_icon"`       //`description:"小程序FICC页icon"`
 	YbFiccPcIcon          string          `gorm:"column:yb_ficc_pc_icon"`    //`description:"小程序PC端FICC页背景图"`
 	YbIconUrl             string          `gorm:"column:yb_icon_url"`        //`description:"小程序已购页icon"`
 	YbBgUrl               string          `gorm:"column:yb_bg_url"`          //`description:"小程序已购详情背景图"`
@@ -136,12 +186,18 @@ type ClassifyList struct {
 	RelateTel             int             `gorm:"column:relate_tel"`         //`description:"是否在电话会中可选: 0-否; 1-是"`
 	RelateVideo           int             `gorm:"column:relate_video"`       //`description:"是否在路演视频中可选: 0-否; 1-是"`
 	Enabled               int             `gorm:"column:enabled"`            //`description:"是否可用,1可用,0禁用"`
+	Level                 int             `gorm:"column:level"`              //`description:"层级"`
+	HasChild              int             `gorm:"column:has_child"`          //`description:"是否有子级别,0:下面没有子分类,1:下面有子分类;默认:0"`
+	ClassifyType          int             `gorm:"column:classify_type"`      //`description:"分类类型:1-研报;2-PPT"`
+	IsRemind              int             `gorm:"column:is_remind"`          //`description:"是否开启提醒:0-关闭;1-开启"`
+	RemindTime            string          `gorm:"column:remind_time"`        //`description:"提醒时间:可选00:00-23:59"`
+	ReportNum             int             `gorm:"column:report_num"`         //`description:"分类下的报告数"`
 	Child                 []*ClassifyList `gorm:"-"`
 	ClassifyMenuId        int             `gorm:"-"` //`description:"二级分类-子目录ID"`
 	ClassifyMenuList      []*ClassifyMenu `gorm:"-"`
-	ChartPermissionIdList []int           `gorm:"-"`                // `description:"绑定的权限ID"`
-	Level                 int             `gorm:"column:level"`     //`description:"层级"`
-	HasChild              int             `gorm:"column:has_child"` //`description:"是否有子级别,0:下面没有子分类,1:下面有子分类;默认:0"`
+	ChartPermissionIdList []int           `gorm:"-"`                                   //`description:"绑定的权限ID"`
+	LevelPath             string          `gorm:"column:level_path" json:"level_path"` //`description:"分类的层级路径,英文逗号分隔"`
+	VisiableUsers         []int           `gorm:"-"`
 }
 
 type ClassifyItem struct {
@@ -334,7 +390,7 @@ func GetCountClassifyChildByParentId(parentId int) (total int, err error) {
 // @param enabled int
 // @return items []*ClassifyList
 // @return err error
-func GetClassifyListByKeyword(keyWord string, enabled int) (items []*ClassifyList, err error) {
+func GetClassifyListByKeyword(keyWord string, enabled, classifyType int) (items []*ClassifyList, err error) {
 	sql := ``
 	pars := make([]interface{}, 0)
 
@@ -342,6 +398,9 @@ func GetClassifyListByKeyword(keyWord string, enabled int) (items []*ClassifyLis
 	if enabled == 1 {
 		sql += ` AND enabled = 1 `
 	}
+	if classifyType > 0 {
+		sql += fmt.Sprintf(` AND classify_type = %d `, classifyType)
+	}
 
 	if keyWord != `` {
 		sql += ` AND classify_name LIKE ? `
@@ -386,3 +445,21 @@ func GetClassifyListByIdList(classifyIdList []int) (items []*Classify, err error
 	err = global.DmSQL["rddp"].Raw(sql, classifyIdList).Find(&items).Error
 	return
 }
+
+func (m *Classify) TableName() string {
+	return "classify"
+}
+
+func (m *Classify) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*Classify, err error) {
+	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 = global.DmSQL["rddp"].Raw(sql, pars...).Find(&items).Error
+	return
+}

+ 87 - 0
models/classify_visible.go

@@ -0,0 +1,87 @@
+package models
+
+import (
+	"eta_gn/eta_api/global"
+	"eta_gn/eta_api/utils"
+	"time"
+)
+
+type ClassifyVisible struct {
+	ClassifyVisibleId int       `gorm:"column:classify_visible_id;primary_key"`
+	ClassifyId        int       `gorm:"column:classify_id"`
+	AdminId           int       `gorm:"column:admin_id"`
+	CreateTime        time.Time `gorm:"column:create_time"`
+}
+
+func (ClassifyVisible) TableName() string {
+	return "classify_visible"
+}
+
+func GetClassifyVisibleAll() (items []*ClassifyVisible, err error) {
+	sql := "SELECT * FROM classify_visible"
+	err = global.DmSQL["rddp"].Raw(sql).Find(&items).Error
+	return
+}
+
+func GetNoVisibleClassifyIdByAdminId(adminId int) (classifyIds []int, err error) {
+	sql := `SELECT classify_id FROM classify_visible GROUP BY classify_id HAVING SUM(CASE WHEN admin_id =? THEN 1 ELSE 0 END) = 0`
+	err = global.DmSQL["rddp"].Raw(sql, adminId).Find(&classifyIds).Error
+	return
+}
+
+func ExtendClassifyVisible(parentId, classifyId int, adminIds []int) (err error) {
+	tx := global.DmSQL["rddp"].Begin()
+	defer func() {
+		if err != nil {
+			tx.Rollback()
+			return
+		}
+		tx.Commit()
+	}()
+	err = tx.Table(ClassifyVisible{}.TableName()).Where("classify_id =?", parentId).Delete(&ClassifyVisible{}).Error
+	insertList := make([]ClassifyVisible, 0, len(adminIds))
+	for _, adminId := range adminIds {
+		classifyVisible := ClassifyVisible{
+			ClassifyId: classifyId,
+			AdminId:    adminId,
+			CreateTime: time.Now(),
+		}
+		insertList = append(insertList, classifyVisible)
+	}
+	err = tx.CreateInBatches(insertList, utils.MultiAddNum).Error
+	return
+}
+
+func UpdateClassifyVisible(classifyId int, adminId []int) (err error) {
+	tx := global.DmSQL["rddp"].Begin()
+	defer func() {
+		if err != nil {
+			tx.Rollback()
+			return
+		}
+		tx.Commit()
+	}()
+	err = tx.Table(ClassifyVisible{}.TableName()).Where("classify_id =?", classifyId).Delete(&ClassifyVisible{}).Error
+	if err != nil {
+		return
+	}
+	if len(adminId) > 0 {
+		insertList := make([]ClassifyVisible, 0, len(adminId))
+		for _, admin := range adminId {
+			classifyVisible := ClassifyVisible{
+				ClassifyId: classifyId,
+				AdminId:    admin,
+				CreateTime: time.Now(),
+			}
+			insertList = append(insertList, classifyVisible)
+		}
+		err = tx.CreateInBatches(insertList, utils.MultiAddNum).Error
+	}
+	return
+}
+
+func GetClassifyVisibleUserIdByClassifyId(classifyId int) (classifyVisibles []int, err error) {
+	db := global.DmSQL["rddp"]
+	err = db.Table(ClassifyVisible{}.TableName()).Where("classify_id =?", classifyId).Pluck("admin_id", &classifyVisibles).Error
+	return
+}

+ 4 - 3
models/data_manage/chart_info.go

@@ -1984,11 +1984,12 @@ type BatchChartRefreshReq struct {
 
 // GetChartInfoListByUniqueCodeSlice 根据图表编码获取图表列表数据
 func GetChartInfoListByUniqueCodeSlice(uniqueCodeSlice []string) (total int64, items []*ChartInfo, err error) {
-	if len(uniqueCodeSlice) <= 0 {
+	num := len(uniqueCodeSlice)
+	if num <= 0 {
 		return
 	}
-	sql := ` SELECT * FROM chart_info WHERE unique_code in ("` + strings.Join(uniqueCodeSlice, `","`) + `") `
-	err = global.DmSQL["data"].Raw(sql).Scan(&items).Error
+	sql := ` SELECT * FROM chart_info WHERE unique_code in (` + utils.GetOrmInReplace(num) + `) `
+	err = global.DmSQL["data"].Raw(sql, uniqueCodeSlice).Scan(&items).Error
 	if err != nil {
 		return
 	}

+ 24 - 4
models/data_manage/edb_info.go

@@ -759,17 +759,30 @@ func GetEdbInfoCalculateListByCondition(condition string, pars []interface{}) (i
 }
 
 // GetEdbInfoAllCalculateByEdbInfoIdList 根据指标id集合 获取基础指标对应的所有计算指标
-func GetEdbInfoAllCalculateByEdbInfoIdList(edbInfoIdList []int) (list []*EdbInfo, err error) {
-	num := len(edbInfoIdList)
+func GetEdbInfoAllCalculateByEdbInfoIdList(fromEdbInfoIdList []int) (list []*EdbInfo, err error) {
+	num := len(fromEdbInfoIdList)
 	if num <= 0 {
 		return
 	}
 
-	sql := ` SELECT DISTINCT b.* FROM edb_info_calculate_mapping AS a
+	edbEdbInfoIdList := make([]int, 0)
+	sql := ` SELECT DISTINCT b.edb_info_id FROM edb_info_calculate_mapping AS a
 			 INNER JOIN edb_info AS b ON a.edb_info_id=b.edb_info_id
              WHERE a.from_edb_info_id in (` + utils.GetOrmInReplace(num) + `)
 			 ORDER BY b.edb_info_id ASC `
-	err = global.DmSQL["data"].Raw(sql, edbInfoIdList).Scan(&list).Error
+	err = global.DmSQL["data"].Raw(sql, fromEdbInfoIdList).Scan(&edbEdbInfoIdList).Error
+	if err != nil {
+		return
+	}
+
+	// 查询来的实际id集合
+	num = len(edbEdbInfoIdList)
+	if num <= 0 {
+		return
+	}
+
+	sql = ` SELECT * FROM  edb_info  WHERE edb_info_id IN (` + utils.GetOrmInReplace(num) + `) ORDER BY edb_info_id ASC `
+	err = global.DmSQL["data"].Raw(sql, edbEdbInfoIdList).Find(&list).Error
 
 	return
 }
@@ -833,6 +846,13 @@ func (edbInfo *EdbInfo) Update(cols []string) (err error) {
 	return
 }
 
+// UpdateById 更新指标基础信息
+func (m *EdbInfo) UpdateById(edbInfoId int, updateMap map[string]interface{}) (err error) {
+	err = global.DmSQL["data"].Model(m).Where("edb_info_id = ? ", edbInfoId).Updates(updateMap).Error
+
+	return
+}
+
 type EdbInfoDataResp struct {
 	EdbInfo  *EdbInfo
 	DataList []*EdbDataList

+ 1 - 1
models/data_manage/edb_info_relation.go

@@ -400,7 +400,7 @@ func ReplaceRelationEdbInfoId(oldEdbInfo, newEdbInfo *EdbInfo, edbRelationIds []
 	}
 
 	// 更新code值
-	sql = ` UPDATE edb_info_relation SET relation_code=CONCAT_WS("_", edb_info_id,refer_object_id,refer_object_type,refer_object_sub_type)  WHERE relation_type=0 ` + sourceWhere + `  and edb_info_relation_id in (` + utils.GetOrmInReplace(len(edbRelationIds)) + `)`
+	sql = ` UPDATE edb_info_relation SET relation_code=CONCAT_WS('_', edb_info_id,refer_object_id,refer_object_type,refer_object_sub_type)  WHERE relation_type=0 ` + sourceWhere + `  and edb_info_relation_id in (` + utils.GetOrmInReplace(len(edbRelationIds)) + `)`
 	err = o.Exec(sql, edbRelationIds).Error
 	if err != nil {
 		return

+ 3 - 0
models/data_manage/excel/request/mixed_table.go

@@ -134,10 +134,13 @@ type MixCellShowStyle struct {
 	Pn              int         `description:"小数点位数增加或减少,正数表述增加,负数表示减少" json:"pn"`
 	Nt              string      `description:"变换类型:number 小数点位数改变,percent百分比," json:"nt"`
 	GlObj           interface{} `description:"公式对象:1:数值,2:百分比,3:文本" json:"glObj"`
+	Width           float64     `description:"单元格宽度" json:"width"`
+	Height          float64     `description:"单元格高度" json:"height"`
 	Decimal         *int        `description:"小数点位数" json:"decimal"`
 	Last            string      `description:"起始操作:nt|decimal" json:"last"`
 	Color           string      `description:"颜色值,#RRG" json:"color"`
 	BackgroundColor string      `description:"背景颜色值,#RRG" json:"background-color"`
+	Align           string      `description:"对齐方式:left|center|right" json:"align"`
 }
 
 type DateDataBeforeAfterReq struct {

+ 9 - 0
models/data_manage/predict_edb_conf.go

@@ -160,3 +160,12 @@ func GetGroupPredictEdbBySourceEdbInfoId(sourceEdbInfoId int) (items []*EdbInfo,
 	err = global.DmSQL["data"].Raw(sql, sourceEdbInfoId).Find(&items).Error
 	return
 }
+
+// GetGroupPredictEdbInfoIdListBySourceEdbInfoId 根据来源指标id获取配置
+func GetGroupPredictEdbInfoIdListBySourceEdbInfoId(sourceEdbInfoId int) (items []int, err error) {
+	sql := ` SELECT a.edb_info_id FROM edb_info AS a 
+	JOIN predict_edb_conf AS b  on a.edb_info_id = b.predict_edb_info_id
+	     WHERE b.source_edb_info_id=? group by a.edb_info_id`
+	err = global.DmSQL["data"].Raw(sql, sourceEdbInfoId).Scan(&items).Error
+	return
+}

+ 1 - 1
models/data_manage/predict_edb_conf_calculate_mapping.go

@@ -81,7 +81,7 @@ func GetPredictEdbConfCalculateMappingDetailListByEdbInfoId(fromEdbInfoIdList []
 
 	sql := ` SELECT a.predict_edb_conf_calculate_mapping_id,a.edb_info_id,a.from_edb_info_id,a.from_edb_code,a.from_source,a.from_source_name,a.sort,a.create_time,a.modify_time,a.from_tag,b.edb_name_source as from_edb_name,b.start_date,b.end_date,b.edb_type,b.edb_code FROM predict_edb_conf_calculate_mapping AS a
 			INNER JOIN edb_info AS b ON a.from_edb_info_id=b.edb_info_id
-			WHERE a.edb_info_id in (` + utils.GetOrmInReplace(num) + `) GROUP BY a.edb_info_id ORDER BY sort ASC `
+			WHERE a.edb_info_id in (` + utils.GetOrmInReplace(num) + `) GROUP BY a.edb_info_id,a.predict_edb_conf_calculate_mapping_id,a.from_edb_info_id,a.from_edb_code,a.from_source,a.from_source_name,a.sort,a.create_time,a.modify_time,a.from_tag,b.edb_name_source,b.start_date,b.end_date,b.edb_type,b.edb_code ORDER BY sort ASC `
 
 	err = global.DmSQL["data"].Raw(sql, fromEdbInfoIdList).Find(&list).Error
 	if err != nil {

+ 69 - 0
models/image_conf.go

@@ -0,0 +1,69 @@
+package models
+
+import (
+	"eta_gn/eta_api/global"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"time"
+)
+
+type ImageConf struct {
+	ImageConfId int       `gorm:"column:image_conf_id;primaryKey;autoIncrement:true"`
+	CreateTime  time.Time `gorm:"column:create_time;autoCreateTime" description:"消息时间"`
+	ModifyTime  time.Time `gorm:"column:modify_time;autoUpdateTime" description:"更新时间"`
+	ImageName   string    `gorm:"column:image_name;type:varchar(64);not null;default:''" description:"图片名称"`
+	Url         string    `gorm:"column:url;type:varchar(512);not null;default:''" description:"图片地址"`
+	ConfType    int       `gorm:"column:conf_type;type:tinyint(4) unsigned;not null;default:1" description:"配置类型 1-ppt素材"`
+	ImageType   int       `gorm:"column:image_type;type:tinyint(4) unsigned;not null;default:1" description:"图片类型 1-封面图 2-背景图 3-封底图"`
+}
+
+type ImageConfPage struct {
+	List   []*ImageConf
+	Paging *paging.PagingItem `description:"分页数据"`
+}
+
+// BatchAddImageMaterials 新增图片素材
+func BatchAddImageMaterials(items []*ImageConf, batchSize int) (err error) {
+	err = global.DmSQL["rddp"].CreateInBatches(items, batchSize).Error
+	return
+}
+
+// GetImageConfByName 根据图片名称查询
+func GetImageConfByName(imageName string) (item *ImageConf, err error) {
+	item = &ImageConf{}
+	err = global.DmSQL["rddp"].Where("image_name = ?", imageName).First(item).Error
+	if err != nil && err.Error() == "record not found" {
+		return nil, nil
+	}
+	return item, nil
+}
+
+// GetImageConfByCondition 根据条件查询图片素材
+func GetImageConfByCondition(condition string, pars []interface{}) (list []*ImageConf, err error) {
+	err = global.DmSQL["rddp"].Where(condition, pars...).Find(&list).Error
+	return
+}
+
+// GetImageConfByConditionCount 根据条件查询图片素材数量
+func GetImageConfByConditionCount(condition string, pars []interface{}) (count int64, err error) {
+	err = global.DmSQL["rddp"].Model(&ImageConf{}).Where(condition, pars...).Count(&count).Error
+	return
+}
+
+// EditImageMaterial 修改图片素材
+func EditImageMaterial(item *ImageConf) (err error) {
+	err = global.DmSQL["rddp"].Updates(item).Error
+	return
+}
+
+// GetImageConfById 根据id查询图片素材
+func GetImageConfById(id int) (item *ImageConf, err error) {
+	item = &ImageConf{}
+	err = global.DmSQL["rddp"].Where("image_conf_id = ?", id).First(item).Error
+	return item, err
+}
+
+// DeleteImageConfByIds 根据id批量删除图片素材
+func DeleteImageConfByIds(ids []int) (err error) {
+	err = global.DmSQL["rddp"].Where("image_conf_id IN ?", ids).Delete(&ImageConf{}).Error
+	return
+}

+ 261 - 41
models/ppt_v2.go

@@ -3,6 +3,8 @@ package models
 import (
 	"eta_gn/eta_api/global"
 	"eta_gn/eta_api/utils"
+	"fmt"
+	"strings"
 	"time"
 
 	"github.com/rdlucklib/rdluck_tools/paging"
@@ -10,27 +12,44 @@ import (
 
 // PptV2 表
 type PptV2 struct {
-	PptId         int       `gorm:"column:ppt_id;primaryKey;autoIncrement:true" description:"ppt的Id"`
-	TemplateType  int       `gorm:"column:template_type" description:"模版类型"`
-	BackgroundImg string    `gorm:"column:background_img" description:"背景图片"`
-	Title         string    `gorm:"column:title" description:"标题"`
-	ReportType    string    `gorm:"column:report_type" description:"报告类型"`
-	PptDate       string    `gorm:"column:ppt_date" description:"选择日期"`
-	Content       string    `gorm:"column:content" description:"ppt内容"`
-	PptUrl        string    `gorm:"column:ppt_url" description:"ppt下载地址"`
-	PptxUrl       string    `gorm:"column:pptx_url" description:"pptx下载地址"`
-	CreateTime    time.Time `gorm:"column:create_time" description:"创建时间"`
-	ModifyTime    time.Time `gorm:"column:modify_time" description:"修改时间"`
-	AdminId       int       `gorm:"column:admin_id" description:"系统用户id"`
-	AdminRealName string    `gorm:"column:admin_real_name" description:"系统用户名称"`
-	PptVersion    int8      `gorm:"column:ppt_version" description:"是否ppt的旧版本;1:旧的,2:新的"`
-	ReportId      int       `gorm:"column:report_id" description:"关联的报告ID"`
-	ReportCode    string    `gorm:"column:report_code" description:"关联的报告code"`
-	IsShare       int8      `gorm:"column:is_share" description:"是否分享,0:不分享,1:分享"`
-	PublishTime   time.Time `gorm:"column:publish_time" description:"发布时间"`
-	CoverContent  string    `gorm:"column:cover_content" description:"PPT内容-JSON"`
-	PptPage       int       `gorm:"column:ppt_page" description:"PPT页数"`
-	TitleSetting  string    `gorm:"column:title_setting" description:"PPT标题设置"`
+	PptId                  int       `gorm:"column:ppt_id;primaryKey;autoIncrement:true" description:"ppt的Id"`
+	TemplateType           int       `gorm:"column:template_type" description:"模版类型"`
+	BackgroundImg          string    `gorm:"column:background_img" description:"现在实际存的封面图(前端改动太大,不知道该字段之前谁定义的)"`
+	Title                  string    `gorm:"column:title" description:"标题"`
+	ReportType             string    `gorm:"column:report_type" description:"报告类型"`
+	PptDate                string    `gorm:"column:ppt_date" description:"选择日期"`
+	Content                string    `gorm:"column:content" description:"ppt内容"`
+	PptUrl                 string    `gorm:"column:ppt_url" description:"ppt下载地址"`
+	PptxUrl                string    `gorm:"column:pptx_url" description:"pptx下载地址"`
+	CreateTime             time.Time `gorm:"column:create_time" description:"创建时间"`
+	ModifyTime             time.Time `gorm:"column:modify_time" description:"修改时间"`
+	AdminId                int       `gorm:"column:admin_id" description:"系统用户id"`
+	AdminRealName          string    `gorm:"column:admin_real_name" description:"系统用户名称"`
+	PptVersion             int8      `gorm:"column:ppt_version" description:"是否ppt的旧版本;1:旧的,2:新的"`
+	ReportId               int       `gorm:"column:report_id" description:"关联的报告ID"`
+	ReportCode             string    `gorm:"column:report_code" description:"关联的报告code"`
+	IsShare                int8      `gorm:"column:is_share" description:"是否分享,0:不分享,1:分享"`
+	PublishTime            time.Time `gorm:"column:publish_time" description:"发布时间"`
+	CoverContent           string    `gorm:"column:cover_content" description:"PPT内容-JSON"`
+	PptPage                int       `gorm:"column:ppt_page" description:"PPT页数"`
+	TitleSetting           string    `gorm:"column:title_setting" description:"PPT标题设置"`
+	ClassifyId             int       `gorm:"column:classify_id" description:"报告分类ID"`
+	AddType                int       `gorm:"column:add_type" description:"新增方式:1-新增报告;2-继承报告"`
+	InheritReportId        int       `gorm:"column:inherit_report_id" description:"继承的报告ID"`
+	CollaborateType        int       `gorm:"column:collaborate_type" description:"协作方式:1-个人;2-多人协作"`
+	CollaborateUsers       string    `gorm:"column:collaborate_users" description:"协作人IDs, 英文逗号分隔"`
+	IsPublicPublish        int       `gorm:"column:is_public_publish" description:"是否公开发布:1-是;2-否"`
+	SubmitTime             time.Time `gorm:"column:submit_time" description:"提交时间"`
+	ApproveTime            time.Time `gorm:"column:approve_time" description:"审批时间"`
+	ReportSource           int       `gorm:"column:report_source" description:"报告来源:1-系统内;2-智力共享"`
+	OutReportId            string    `gorm:"column:out_report_id" description:"外部报告ID(或编码)"`
+	State                  int       `gorm:"column:state" description:"报告状态:1-未发布;2-已发布;3-待提交;4-待审批;5-已驳回;6-已通过"`
+	TopicEndTime           time.Time `gorm:"column:topic_end_time" description:"课题结束时间"`
+	CurrentBackgroundImg   string    `gorm:"column:current_background_img" description:"背景图片"`
+	BackCoverImg           string    `gorm:"column:back_cover_img" description:"封底图片"`
+	CurrentBackgroundImgId int       `gorm:"column:current_background_img_id" description:"背景图片id"`
+	BackCoverImgId         int       `gorm:"column:back_cover_img_id" description:"封底图片id"`
+	BackgroundImgId        int       `gorm:"column:background_img_id" description:"封面图id"`
 }
 
 type PptV2Item struct {
@@ -113,12 +132,17 @@ func DeletePptV2(pptId int) (err error) {
 type AddPptV2Req struct {
 	PptId     int64 `description:"ppt_id"`
 	FirstPage struct {
-		Title        string `description:"标题"`
-		ReportType   string `description:"类型"`
-		PptDate      string `description:"日期"`
-		ImgUrl       string `description:"图片"`
-		BackIndex    int    `description:"背景图片下标"`
-		TemplateType int    `description:"模版id"`
+		Title                  string `description:"标题"`
+		ReportType             string `description:"类型"`
+		PptDate                string `description:"日期"`
+		ImgUrl                 string `description:"封面图片"`
+		CurrentBackgroundImg   string `description:"背景图片"`
+		BackCoverImg           string `description:"封底图片"`
+		CurrentBackgroundImgId int    `description:"背景图片id"`
+		BackCoverImgId         int    `description:"封底图片id"`
+		BackgroundImgId        int    `description:"封面图id"`
+		BackIndex              int    `description:"背景图片下标"`
+		TemplateType           int    `description:"模版id"`
 	} `description:"首页"`
 	Content      string `description:"ppt的json数据"`
 	GroupId      int64  `description:"目录id"`
@@ -181,19 +205,21 @@ func AddPptV2PublishRecord(item *PptV2PublishRecord) (lastId int64, err error) {
 
 // PptV2SaveLog ppt记录表
 type PptV2SaveLog struct {
-	Id            int       `gorm:"column:id;primaryKey;autoIncrement" description:"自增Id"`
-	PptId         int       `gorm:"column:ppt_id" description:"ppt的Id"`
-	TemplateType  int       `gorm:"column:template_type" description:"模版类型"`
-	BackgroundImg string    `gorm:"column:background_img" description:"背景图片"`
-	Title         string    `gorm:"column:title" description:"标题"`
-	ReportType    string    `gorm:"column:report_type" description:"报告类型"`
-	PptDate       string    `gorm:"column:ppt_date" description:"选择日期"`
-	Content       string    `gorm:"column:content" description:"ppt内容"`
-	AdminId       int       `gorm:"column:admin_id" description:"系统用户id"`
-	AdminRealName string    `gorm:"column:admin_real_name" description:"系统用户名称"`
-	CreateTime    time.Time `gorm:"column:create_time" description:"创建时间"`
-	CoverContent  string    `gorm:"column:cover_content" description:"PPT内容-JSON"`
-	TitleSetting  string    `gorm:"column:title_setting" description:"PPT标题设置"`
+	Id                   int       `gorm:"column:id;primaryKey;autoIncrement" description:"自增Id"`
+	PptId                int       `gorm:"column:ppt_id" description:"ppt的Id"`
+	TemplateType         int       `gorm:"column:template_type" description:"模版类型"`
+	BackgroundImg        string    `gorm:"column:background_img" description:"背景图片"`
+	Title                string    `gorm:"column:title" description:"标题"`
+	ReportType           string    `gorm:"column:report_type" description:"报告类型"`
+	PptDate              string    `gorm:"column:ppt_date" description:"选择日期"`
+	Content              string    `gorm:"column:content" description:"ppt内容"`
+	AdminId              int       `gorm:"column:admin_id" description:"系统用户id"`
+	AdminRealName        string    `gorm:"column:admin_real_name" description:"系统用户名称"`
+	CreateTime           time.Time `gorm:"column:create_time" description:"创建时间"`
+	CoverContent         string    `gorm:"column:cover_content" description:"PPT内容-JSON"`
+	TitleSetting         string    `gorm:"column:title_setting" description:"PPT标题设置"`
+	CurrentBackgroundImg string    `gorm:"column:current_background_img" description:"背景图片"`
+	BackCoverImg         string    `gorm:"column:back_cover_img" description:"封底图片"`
 }
 
 // AddPptV2SaveLog 新增PPT日志
@@ -289,7 +315,8 @@ type PPTEditingReq struct {
 // PPTDetailResp PPT详情响应体
 type PPTDetailResp struct {
 	*PptV2
-	Editor PPTEditingCache `description:"编辑人信息"`
+	Editor  PPTEditingCache `description:"编辑人信息"`
+	HasAuth bool            `description:"是否有权限"`
 }
 
 // PPTEditingCache PPT编辑缓存信息
@@ -299,3 +326,196 @@ type PPTEditingCache struct {
 	Editor    string `description:"编辑者姓名"`
 	Tips      string `description:"提示信息"`
 }
+
+func (m *PptV2) TableName() string {
+	return "ppt_v2"
+}
+
+func (m *PptV2) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*PptV2, err error) {
+	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 = global.DmSQL["rddp"].Raw(sql, pars...).Find(&items).Error
+	return
+}
+
+func (m *PptV2) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*PptV2, err error) {
+	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)
+	pars = append(pars, startSize, pageSize)
+	err = global.DmSQL["rddp"].Raw(sql, pars...).Find(&items).Error
+	return
+}
+
+func (m *PptV2) GetPageItemsByConditionWithAdminId(condition string, pars []interface{}, fieldArr []string, orderRule string, adminId, startSize, pageSize int) (items []*PptV2, err error) {
+	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 AND classify_id NOT IN (
+		SELECT DISTINCT "classify_id" FROM "classify_visible") %s
+	UNION ALL
+	SELECT %s FROM %s WHERE 1=1 AND classify_id IN (
+		SELECT DISTINCT "classify_id" FROM "classify_visible" WHERE "admin_id" = %d) %s %s LIMIT ?,? `, fields, m.TableName(), condition, fields, m.TableName(), adminId, condition, order)
+	pars = append(pars, pars...)
+	pars = append(pars, startSize, pageSize)
+	err = global.DmSQL["rddp"].Raw(sql, pars...).Find(&items).Error
+	return
+}
+
+func (m *PptV2) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM %s WHERE 1=1 %s`, m.TableName(), condition)
+	err = global.DmSQL["rddp"].Raw(sql, pars...).Scan(&count).Error
+	return
+}
+
+func (m *PptV2) GetCountByConditionWithAdminId(condition string, pars []interface{}, adminId int) (count int, err error) {
+	sql := fmt.Sprintf(`
+	SELECT (
+		(SELECT COUNT(1) FROM %s WHERE 1=1 AND classify_id NOT IN (
+			SELECT DISTINCT "classify_id" FROM "classify_visible") %s)
+		+
+		(SELECT COUNT(1) FROM %s WHERE 1=1 AND classify_id IN (
+			SELECT DISTINCT "classify_id" FROM "classify_visible" WHERE "admin_id" = %d) %s)
+	) AS count `, m.TableName(), condition, m.TableName(), adminId, condition)
+	pars = append(pars, pars...)
+	err = global.DmSQL["rddp"].Raw(sql, pars...).Scan(&count).Error
+	return
+}
+
+// PptReportQueryFields 除富文本的常用查询字段
+var PptReportQueryFields = []string{
+	"ppt_id", "title", "classify_id", "ppt_version", "pptx_url", "ppt_page", "title_setting", "state", "report_source", "publish_time", "submit_time", "approve_time", "create_time", "modify_time", "admin_id", "admin_real_name", "collaborate_type", "collaborate_users",
+}
+
+type PptReportItem struct {
+	PptId      int    `description:"主键Id"`
+	Title      string `description:"标题"`
+	ClassifyId int    `description:"报告分类ID"`
+	PptVersion int    `description:"是否ppt的旧版本;1:旧的,2:新的"`
+	PptxUrl    string `description:"pptx下载地址"`
+	PptPage    int    `description:"PPT总页数"`
+	//Content      string `description:"内容"`
+	TitleSetting     string                     `description:"PPT标题设置"`
+	State            int                        `description:"报告状态:1-未发布;2-已发布;3-待提交;4-待审批;5-已驳回;6-已通过"`
+	ReportSource     int                        `description:"报告来源:1-系统内;2-智力共享"`
+	PublishTime      string                     `description:"发布时间"`
+	SubmitTime       string                     `description:"提交时间"`
+	ApproveTime      string                     `description:"审批时间"`
+	CreateTime       string                     `description:"创建时间"`
+	ModifyTime       string                     `description:"更新时间"`
+	FullClassify     string                     `description:"分类完整路径, /分隔"`
+	CollaborateType  int                        `description:"协作方式:1-个人;2-多人协作"`
+	CollaborateUsers []PptReportCollaborateUser `description:"协作人信息"`
+	HasAuth          bool                       `description:"是否创建人/协作人"`
+	Editor           PPTEditingCache            `description:"编辑人信息"`
+	AdminId          int                        `description:"创建人ID"`
+	AdminRealName    string                     `description:"创建人姓名"`
+}
+
+type PptReportCollaborateUser struct {
+	AdminId  int    `description:"用户ID"`
+	RealName string `description:"用户姓名"`
+}
+
+func (m *PptV2) Format2ReportItem(origin *PptV2) (item *PptReportItem) {
+	if origin == nil {
+		return
+	}
+	item = new(PptReportItem)
+	item.PptId = origin.PptId
+	item.Title = origin.Title
+	item.ClassifyId = origin.ClassifyId
+	item.PptVersion = int(origin.PptVersion)
+	item.PptxUrl = origin.PptxUrl
+	item.PptPage = origin.PptPage
+	//item.Content = origin.Content
+	item.TitleSetting = origin.TitleSetting
+	item.State = origin.State
+	item.ReportSource = origin.ReportSource
+	item.PublishTime = utils.TimeTransferString(utils.FormatDateTime, origin.PublishTime)
+	item.SubmitTime = utils.TimeTransferString(utils.FormatDateTime, origin.SubmitTime)
+	item.ApproveTime = utils.TimeTransferString(utils.FormatDateTime, origin.ApproveTime)
+	item.CreateTime = utils.TimeTransferString(utils.FormatDateTime, origin.CreateTime)
+	item.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, origin.ModifyTime)
+	item.AdminId = origin.AdminId
+	item.AdminRealName = origin.AdminRealName
+	item.CollaborateType = origin.CollaborateType
+	return
+}
+
+type PptReportClassifyItem struct {
+	Id           int                      `description:"分类ID"`
+	ClassifyName string                   `description:"分类名称"`
+	Sort         int                      `description:"排序"`
+	ParentId     int                      `description:"父级分类ID"`
+	Enabled      int                      `description:"是否可用,1可用,0禁用"`
+	Level        int                      `description:"层级"`
+	HasChild     int                      `description:"是否有子级别,0:下面没有子分类,1:下面有子分类;默认:0"`
+	ClassifyType int                      `description:"分类类型:1-研报;2-PPT"`
+	ReportNum    int                      `description:"分类下的报告数"`
+	PptList      []*PptReportItem         `description:"ppt报告"`
+	Child        []*PptReportClassifyItem `description:"子分类"`
+}
+
+type PptPageReportResp struct {
+	List   []*PptReportItem
+	Paging *paging.PagingItem `description:"分页数据"`
+}
+
+type PptReportCreateReq struct {
+	Title              string `description:"标题"`
+	ClassifyId         int    `description:"分类ID"`
+	AddType            int    `description:"新增方式:1:新增报告,2:继承报告"`
+	InheritPptId       int    `description:"待继承的报告ID"`
+	CollaborateType    int    `description:"协作方式,1:个人,2:多人协作。默认:1"`
+	CollaborateUserIds []int  `description:"协作人IDs"`
+	FirstPage          struct {
+		Title                  string `description:"标题"`
+		ReportType             string `description:"类型"`
+		PptDate                string `description:"日期"`
+		ImgUrl                 string `description:"图片"`
+		CurrentBackgroundImg   string `description:"背景图片"`
+		BackCoverImg           string `description:"封底图片"`
+		CurrentBackgroundImgId int    `description:"背景图片id"`
+		BackCoverImgId         int    `description:"封底图片id"`
+		BackgroundImgId        int    `description:"封面图id"`
+		BackIndex              int    `description:"背景图片下标"`
+		TemplateType           int    `description:"模版id"`
+	} `description:"首页"`
+	Content      string `description:"ppt的json数据"`
+	CoverContent string `description:"封面图内容-JSON数据"`
+	TitleSetting string `description:"PPT标题设置"`
+}
+
+// MovePptReportClassify 转移PPT报告分类
+func MovePptReportClassify(newClassifyId, originClassifyId int) (err error) {
+	sql := `UPDATE ppt_v2 SET classify_id = ? WHERE classify_id = ?`
+	err = global.DmSQL["rddp"].Exec(sql, newClassifyId, originClassifyId).Error
+	return
+}
+
+// GetPptByImageIdCount 根据图片id查询ppt信息
+func GetPptByImageIdCount(imageId int) (count int, err error) {
+	sql := `SELECT COUNT(1) FROM ppt_v2 WHERE current_background_img_id = ? or back_cover_img_id = ? or background_img_id = ?`
+	err = global.DmSQL["rddp"].Raw(sql, imageId, imageId, imageId).Scan(&count).Error
+	return
+}

+ 89 - 0
models/ppt_v2_history.go

@@ -0,0 +1,89 @@
+package models
+
+import (
+	"eta_gn/eta_api/global"
+	"fmt"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"time"
+)
+
+type PptV2History struct {
+	Id            int       `gorm:"primaryKey"`
+	PptId         int       `description:"ppt ID"`
+	TemplateType  int       `description:"模板类型"`
+	BackgroundImg string    `description:"背景图"`
+	Title         string    `description:"标题"`
+	ReportType    string    `description:"报告类型"`
+	PptDate       string    `description:"选择日期"`
+	Content       string    `description:"内容"`
+	AdminId       int       `description:"操作人ID"`
+	AdminRealName string    `description:"操作人真实姓名"`
+	CreateTime    time.Time `description:"创建时间"`
+	CoverContent  string    `description:"封面内容"`
+	TitleSetting  string    `description:"标题设置"`
+}
+
+// 获取表名
+func (p *PptV2History) TableName() string {
+	return "ppt_v2_history"
+}
+
+func (p *PptV2History) Add() (err error) {
+	err = global.DmSQL["rddp"].Create(p).Error
+	return
+}
+
+func (p *PptV2History) GetNoContentPageList(condition string, pars []interface{}, startSize, pageSize int) (list []*PptV2History, err error) {
+	sql := fmt.Sprintf(`SELECT id, ppt_id, template_type, background_img, title, report_type, ppt_date, create_time, admin_id, admin_real_name  FROM %s WHERE 1=1 `, p.TableName())
+	if condition != "" {
+		sql += condition
+	}
+	sql += ` ORDER BY create_time DESC, id DESC LIMIT ?, ?`
+	pars = append(pars, startSize, pageSize)
+	err = global.DmSQL["rddp"].Raw(sql, pars...).Scan(&list).Error
+	return
+}
+
+func (p *PptV2History) GetPageListCount(condition string, pars []interface{}) (count int, err error) {
+	sql := fmt.Sprintf(`SELECT COUNT(1) AS count  FROM %s WHERE 1=1 `, p.TableName())
+	if condition != "" {
+		sql += condition
+	}
+	err = global.DmSQL["rddp"].Raw(sql, pars...).Scan(&count).Error
+	return
+}
+
+// 删除接口
+func (p *PptV2History) Delete() (err error) {
+	err = global.DmSQL["rddp"].Delete(p).Error
+	return
+}
+
+// 查询单条记录
+func (p *PptV2History) GetById(id int) (item *PptV2History, err error) {
+	err = global.DmSQL["rddp"].Where("id = ?", id).First(&item).Error
+	return
+}
+
+// PptV2HistoryListItem 定义PPT历史记录列表项的结构体
+type PptV2HistoryListItem struct {
+	Id            int    `description:"PPT历史记录id"`
+	PptId         int    `description:"ppt ID"`
+	TemplateType  int    `description:"模板类型"`
+	BackgroundImg string `description:"背景图"`
+	Title         string `description:"标题"`
+	ReportType    string `description:"报告类型"`
+	PptDate       string `description:"选择日期"`
+	AdminId       int    `description:"操作人ID"`
+	AdminRealName string `description:"操作人真实姓名"`
+	CreateTime    string `description:"创建时间"`
+}
+
+type PptV2HistoryListResp struct {
+	List   []*PptV2HistoryListItem
+	Paging *paging.PagingItem `description:"分页数据"`
+}
+
+type DeletePptV2HistoryReq struct {
+	Id int `description:"PPT历史记录id"`
+}

+ 220 - 57
models/report.go

@@ -6,10 +6,11 @@ import (
 	"eta_gn/eta_api/global"
 	"eta_gn/eta_api/utils"
 	"fmt"
-	"github.com/rdlucklib/rdluck_tools/paging"
-	"gorm.io/gorm"
 	"strings"
 	"time"
+
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"gorm.io/gorm"
 )
 
 // 报告状态
@@ -69,26 +70,31 @@ type Report struct {
 	DetailImgUrl       string    `gorm:"column:detail_img_url" description:"报告详情长图地址"`
 	DetailPdfUrl       string    `gorm:"column:detail_pdf_url" description:"报告详情PDF地址"`
 
-	ContentStruct       string    `gorm:"column:content_struct" description:"内容组件"`
-	LastModifyAdminId   int       `gorm:"column:last_modify_admin_id" description:"最后更新人ID"`
-	LastModifyAdminName string    `gorm:"column:last_modify_admin_name" description:"最后更新人姓名"`
-	ContentModifyTime   time.Time `gorm:"column:content_modify_time" description:"内容更新时间"`
-	Pv                  int       `gorm:"column:pv" description:"pv"`
-	Uv                  int       `gorm:"column:uv" description:"uv"`
-	HeadImg             string    `gorm:"column:head_img" description:"报告头图地址"`
-	EndImg              string    `gorm:"column:end_img" description:"报告尾图地址"`
-	CanvasColor         string    `gorm:"column:canvas_color" description:"画布颜色"`
-	NeedSplice          int       `gorm:"column:need_splice" description:"是否拼接版头版位的标记,主要是为了兼容历史报告。0-不需要 1-需要"`
-	HeadResourceId      int       `gorm:"column:head_resource_id" description:"版头资源ID"`
-	EndResourceId       int       `gorm:"column:end_resource_id" description:"版尾资源ID"`
-	ClassifyIdThird     int       `gorm:"column:classify_id_third" description:"三级分类id"`
-	ClassifyNameThird   string    `gorm:"column:classify_name_third" description:"三级分类名称"`
-	CollaborateType     int8      `gorm:"column:collaborate_type" description:"协作方式,1:个人,2:多人协作。默认:1"`
-	ReportLayout        int8      `gorm:"column:report_layout" description:"报告布局,1:常规布局,2:智能布局。默认:1"`
-	IsPublicPublish     int8      `gorm:"column:is_public_publish" description:"是否公开发布,1:是,2:否"`
-	ReportCreateTime    time.Time `gorm:"column:report_create_time" description:"报告时间创建时间"`
-	InheritReportId     int       `gorm:"column:inherit_report_id" description:"待继承的报告ID"`
-	VoiceGenerateType   int       `gorm:"column:voice_generate_type" description:"音频生成方式,0:系统生成,1:人工上传"`
+	ContentStruct       string     `gorm:"column:content_struct" description:"内容组件"`
+	LastModifyAdminId   int        `gorm:"column:last_modify_admin_id" description:"最后更新人ID"`
+	LastModifyAdminName string     `gorm:"column:last_modify_admin_name" description:"最后更新人姓名"`
+	ContentModifyTime   time.Time  `gorm:"column:content_modify_time" description:"内容更新时间"`
+	Pv                  int        `gorm:"column:pv" description:"pv"`
+	Uv                  int        `gorm:"column:uv" description:"uv"`
+	HeadImg             string     `gorm:"column:head_img" description:"报告头图地址"`
+	EndImg              string     `gorm:"column:end_img" description:"报告尾图地址"`
+	CanvasColor         string     `gorm:"column:canvas_color" description:"画布颜色"`
+	NeedSplice          int        `gorm:"column:need_splice" description:"是否拼接版头版位的标记,主要是为了兼容历史报告。0-不需要 1-需要"`
+	HeadResourceId      int        `gorm:"column:head_resource_id" description:"版头资源ID"`
+	EndResourceId       int        `gorm:"column:end_resource_id" description:"版尾资源ID"`
+	ClassifyIdThird     int        `gorm:"column:classify_id_third" description:"三级分类id"`
+	ClassifyNameThird   string     `gorm:"column:classify_name_third" description:"三级分类名称"`
+	CollaborateType     int8       `gorm:"column:collaborate_type" description:"协作方式,1:个人,2:多人协作。默认:1"`
+	ReportLayout        int8       `gorm:"column:report_layout" description:"报告布局,1:常规布局,2:智能布局。默认:1"`
+	IsPublicPublish     int8       `gorm:"column:is_public_publish" description:"是否公开发布,1:是,2:否"`
+	ReportCreateTime    time.Time  `gorm:"column:report_create_time" description:"报告时间创建时间"`
+	InheritReportId     int        `gorm:"column:inherit_report_id" description:"待继承的报告ID"`
+	VoiceGenerateType   int        `gorm:"column:voice_generate_type" description:"音频生成方式,0:系统生成,1:人工上传"`
+	ReportSource        int        `gorm:"column:report_source" description:"报告来源:1-系统内;2-智力共享"`
+	OutReportId         string     `gorm:"column:out_report_id" description:"外部报告ID(或编码)"`
+	PrePublishTime      *time.Time `gorm:"column:pre_publish_time" description:"预发布时间"`
+	PreMsgSend          int        `gorm:"column:pre_msg_send" description:"定时发布成功后是否立即推送模版消息,0:未发送,1:已发送"`
+	TopicEndTime        time.Time  `gorm:"column:topic_end_time" description:"课题结束时间"`
 }
 
 type ReportList struct {
@@ -151,6 +157,7 @@ type ReportList struct {
 	ClassifyIdThird     int               `gorm:"column:classify_id_third" description:"三级分类id"`
 	ClassifyNameThird   string            `gorm:"column:classify_name_third" description:"三级分类名称"`
 	InheritReportId     int               `gorm:"column:inherit_report_id" description:"待继承的报告ID"`
+	ReportSource        int               `gorm:"column:report_source" description:"报告来源:1-系统内;2-智力共享"`
 }
 
 type ReportListResp struct {
@@ -175,6 +182,34 @@ func GetReportListCountV1(condition string, pars []interface{}) (count int, err
 	return
 }
 
+// func GetReportListCountV1(condition string, pars []interface{}) (count int, err error) {
+// 	sql := `SELECT COUNT(1) AS count  FROM report as a
+// 	LEFT JOIN classify_visible c ON (
+// 		CASE
+// 			WHEN a.classify_id_third > 0 THEN a.classify_id_third
+// 			WHEN a.classify_id_second > 0 THEN a.classify_id_second
+// 			ELSE a.classify_id_first
+// 		END
+// 	) = c.classify_id AND c.admin_id =?
+// 	WHERE 1=1 AND (c.admin_id IS NULL OR c.admin_id =?) AND a.id NOT IN (
+// 		SELECT id FROM report ta
+// 		JOIN classify_visible tb ON (
+// 			CASE
+// 				WHEN ta.classify_id_third > 0 THEN ta.classify_id_third
+// 				WHEN ta.classify_id_second > 0 THEN ta.classify_id_second
+// 				ELSE ta.classify_id_first
+// 			END
+// 		) = tb.classify_id
+// 		GROUP BY ta."id"
+// 		HAVING SUM(CASE WHEN tb.admin_id = ? THEN 1 ELSE 0 END) = 0
+// 	) `
+// 	if condition != "" {
+// 		sql += condition
+// 	}
+// 	err = global.DmSQL["rddp"].Raw(sql, pars...).Scan(&count).Error
+// 	return
+// }
+
 // GetReportListV1
 // @Description: 获取普通报告列表的数据
 // @author: Roc
@@ -198,6 +233,38 @@ func GetReportListV1(condition string, pars []interface{}, startSize, pageSize i
 	return
 }
 
+// func GetReportListV1(condition string, pars []interface{}, startSize, pageSize int) (items []*ReportList, err error) {
+// 	sql := `SELECT * FROM report as a
+// 	LEFT JOIN classify_visible c ON (
+// 		CASE
+// 			WHEN a.classify_id_third > 0 THEN a.classify_id_third
+// 			WHEN a.classify_id_second > 0 THEN a.classify_id_second
+// 			ELSE a.classify_id_first
+// 		END
+// 	) = c.classify_id AND c.admin_id =?
+// 	WHERE 1=1 AND (c.admin_id IS NULL OR c.admin_id =?) AND a.id NOT IN (
+// 		SELECT id FROM report ta
+// 		JOIN classify_visible tb ON (
+// 			CASE
+// 				WHEN ta.classify_id_third > 0 THEN ta.classify_id_third
+// 				WHEN ta.classify_id_second > 0 THEN ta.classify_id_second
+// 				ELSE ta.classify_id_first
+// 			END
+// 		) = tb.classify_id
+// 		GROUP BY ta."id"
+// 		HAVING SUM(CASE WHEN tb.admin_id = ? THEN 1 ELSE 0 END) = 0
+// 	) `
+// 	if condition != "" {
+// 		sql += condition
+// 	}
+// 	// 排序:1:未发布;2:已发布;3-待提交;4-待审批;5-已驳回;6-已通过
+// 	sql += `ORDER BY FIELD(state,3,1,4,5,6,2), modify_time DESC LIMIT ?,?`
+// 	pars = append(pars, startSize)
+// 	pars = append(pars, pageSize)
+// 	err = global.DmSQL["rddp"].Raw(sql, pars...).Find(&items).Error
+// 	return
+// }
+
 type ReportPvUv struct {
 	ReportId int
 	PvTotal  int
@@ -225,7 +292,7 @@ func GetReportPvUvByReportIdList(reportIdList []int) (items []ReportPvUv, err er
 func GetReportListCountByGrant(condition string, pars []interface{}) (count int, err error) {
 	sql := `SELECT a.id  FROM report as a 
     JOIN report_grant b on a.id=b.report_id 
- WHERE 1=1  `
+	WHERE 1=1  `
 	if condition != "" {
 		sql += condition
 	}
@@ -236,6 +303,39 @@ func GetReportListCountByGrant(condition string, pars []interface{}) (count int,
 	return
 }
 
+// func GetReportListCountByGrant(condition string, pars []interface{}) (count int, err error) {
+// 	sql := `SELECT a.id  FROM report as a
+//     JOIN report_grant b on a.id=b.report_id
+// 	LEFT JOIN classify_visible c ON (
+// 		CASE
+// 			WHEN a.classify_id_third > 0 THEN a.classify_id_third
+// 			WHEN a.classify_id_second > 0 THEN a.classify_id_second
+// 			ELSE a.classify_id_first
+// 		END
+// 	) = c.classify_id AND c.admin_id =?
+// 	WHERE 1=1 AND (c.admin_id IS NULL OR c.admin_id =?) AND a.id NOT IN (
+// 		SELECT id FROM report ta
+// 		JOIN classify_visible tb ON (
+// 			CASE
+// 				WHEN ta.classify_id_third > 0 THEN ta.classify_id_third
+// 				WHEN ta.classify_id_second > 0 THEN ta.classify_id_second
+// 				ELSE ta.classify_id_first
+// 			END
+// 		) = tb.classify_id
+// 		GROUP BY ta."id"
+// 		HAVING SUM(CASE WHEN tb.admin_id = ? THEN 1 ELSE 0 END) = 0
+// 	) `
+// 	if condition != "" {
+// 		sql += condition
+// 	}
+// 	sql += " GROUP BY a.id "
+
+// 	sql = `SELECT COUNT(1) AS count  FROM (` + sql + `) d`
+
+// 	err = global.DmSQL["rddp"].Raw(sql, pars...).Scan(&count).Error
+// 	return
+// }
+
 // GetReportListByGrant
 // @Description: 获取共享报告列表的数据
 // @author: Roc
@@ -247,12 +347,12 @@ func GetReportListCountByGrant(condition string, pars []interface{}) (count int,
 // @return items []*ReportList
 // @return err error
 func GetReportListByGrant(condition string, pars []interface{}, startSize, pageSize int) (items []*ReportList, err error) {
-	sql := `SELECT a.id,a.add_type,a.classify_id_first,a.classify_name_first,a.classify_id_second,a.classify_name_second,a.title,a.abstract,a.author,a.frequency,a.create_time,a.modify_time,a.state,a.publish_time,a.pre_publish_time,a.stage,a.msg_is_send,a.pre_msg_send,a.video_url,a.video_name,a.video_play_seconds,a.report_code,a.video_size,a.report_version,a.ths_msg_is_send,a.has_chapter,a.chapter_type,a.old_report_id,a.msg_send_time,a.admin_id,a.admin_real_name,a.approve_time,a.approve_id,a.detail_img_url,a.detail_pdf_url,a.last_modify_admin_id,a.last_modify_admin_name,a.content_modify_time,a.pv,a.uv,a.canvas_color,a.need_splice,a.head_resource_id,a.end_resource_id,a.classify_id_third,a.classify_name_third,a.collaborate_type,a.report_layout,a.is_public_publish,a.report_create_time,a.inherit_report_id,a.voice_generate_type FROM report as a JOIN report_grant b on a.id = b.report_id  WHERE 1=1  `
+	sql := `SELECT a.id,a.add_type,a.classify_id_first,a.classify_name_first,a.classify_id_second,a.classify_name_second,a.title,a.abstract,a.author,a.frequency,a.create_time,a.modify_time,a.state,a.publish_time,a.pre_publish_time,a.stage,a.msg_is_send,a.pre_msg_send,a.video_url,a.video_name,a.video_play_seconds,a.report_code,a.video_size,a.report_version,a.ths_msg_is_send,a.has_chapter,a.chapter_type,a.old_report_id,a.msg_send_time,a.admin_id,a.admin_real_name,a.approve_time,a.approve_id,a.detail_img_url,a.detail_pdf_url,a.last_modify_admin_id,a.last_modify_admin_name,a.content_modify_time,a.pv,a.uv,a.canvas_color,a.need_splice,a.head_resource_id,a.end_resource_id,a.classify_id_third,a.classify_name_third,a.collaborate_type,a.report_layout,a.is_public_publish,a.report_create_time,a.inherit_report_id,a.voice_generate_type,a.report_source FROM report as a JOIN report_grant b on a.id = b.report_id  WHERE 1=1  `
 	if condition != "" {
 		sql += condition
 	}
 	// 排序:1:未发布;2:已发布;3-待提交;4-待审批;5-已驳回;6-已通过
-	sql += ` GROUP BY a.id,a.add_type,a.classify_id_first,a.classify_name_first,a.classify_id_second,a.classify_name_second,a.title,a.abstract,a.author,a.frequency,a.create_time,a.modify_time,a.state,a.publish_time,a.pre_publish_time,a.stage,a.msg_is_send,a.pre_msg_send,a.video_url,a.video_name,a.video_play_seconds,a.report_code,a.video_size,a.report_version,a.ths_msg_is_send,a.has_chapter,a.chapter_type,a.old_report_id,a.msg_send_time,a.admin_id,a.admin_real_name,a.approve_time,a.approve_id,a.detail_img_url,a.detail_pdf_url,a.last_modify_admin_id,a.last_modify_admin_name,a.content_modify_time,a.pv,a.uv,a.canvas_color,a.need_splice,a.head_resource_id,a.end_resource_id,a.classify_id_third,a.classify_name_third,a.collaborate_type,a.report_layout,a.is_public_publish,a.report_create_time,a.inherit_report_id,a.voice_generate_type
+	sql += ` GROUP BY a.id,a.add_type,a.classify_id_first,a.classify_name_first,a.classify_id_second,a.classify_name_second,a.title,a.abstract,a.author,a.frequency,a.create_time,a.modify_time,a.state,a.publish_time,a.pre_publish_time,a.stage,a.msg_is_send,a.pre_msg_send,a.video_url,a.video_name,a.video_play_seconds,a.report_code,a.video_size,a.report_version,a.ths_msg_is_send,a.has_chapter,a.chapter_type,a.old_report_id,a.msg_send_time,a.admin_id,a.admin_real_name,a.approve_time,a.approve_id,a.detail_img_url,a.detail_pdf_url,a.last_modify_admin_id,a.last_modify_admin_name,a.content_modify_time,a.pv,a.uv,a.canvas_color,a.need_splice,a.head_resource_id,a.end_resource_id,a.classify_id_third,a.classify_name_third,a.collaborate_type,a.report_layout,a.is_public_publish,a.report_create_time,a.inherit_report_id,a.voice_generate_type,a.report_source
 
 		ORDER BY CASE a."state"   
         WHEN 3 THEN 1  
@@ -268,6 +368,47 @@ func GetReportListByGrant(condition string, pars []interface{}, startSize, pageS
 	return
 }
 
+// func GetReportListByGrant(condition string, pars []interface{}, startSize, pageSize int) (items []*ReportList, err error) {
+// 	sql := `SELECT a.id,a.add_type,a.classify_id_first,a.classify_name_first,a.classify_id_second,a.classify_name_second,a.title,a.abstract,a.author,a.frequency,a.create_time,a.modify_time,a.state,a.publish_time,a.pre_publish_time,a.stage,a.msg_is_send,a.pre_msg_send,a.video_url,a.video_name,a.video_play_seconds,a.report_code,a.video_size,a.report_version,a.ths_msg_is_send,a.has_chapter,a.chapter_type,a.old_report_id,a.msg_send_time,a.admin_id,a.admin_real_name,a.approve_time,a.approve_id,a.detail_img_url,a.detail_pdf_url,a.last_modify_admin_id,a.last_modify_admin_name,a.content_modify_time,a.pv,a.uv,a.canvas_color,a.need_splice,a.head_resource_id,a.end_resource_id,a.classify_id_third,a.classify_name_third,a.collaborate_type,a.report_layout,a.is_public_publish,a.report_create_time,a.inherit_report_id,a.voice_generate_type,a.report_source FROM report as a JOIN report_grant b on a.id = b.report_id
+// 	LEFT JOIN classify_visible c ON (
+// 		CASE
+// 			WHEN a.classify_id_third > 0 THEN a.classify_id_third
+// 			WHEN a.classify_id_second > 0 THEN a.classify_id_second
+// 			ELSE a.classify_id_first
+// 		END
+// 	) = c.classify_id AND c.admin_id =?
+// 	WHERE 1=1 AND (c.admin_id IS NULL OR c.admin_id =?) AND a.id NOT IN (
+// 		SELECT id FROM report ta
+// 		JOIN classify_visible tb ON (
+// 			CASE
+// 				WHEN ta.classify_id_third > 0 THEN ta.classify_id_third
+// 				WHEN ta.classify_id_second > 0 THEN ta.classify_id_second
+// 				ELSE ta.classify_id_first
+// 			END
+// 		) = tb.classify_id
+// 		GROUP BY ta."id"
+// 		HAVING SUM(CASE WHEN tb.admin_id = ? THEN 1 ELSE 0 END) = 0
+// 	) `
+// 	if condition != "" {
+// 		sql += condition
+// 	}
+// 	// 排序:1:未发布;2:已发布;3-待提交;4-待审批;5-已驳回;6-已通过
+// 	sql += ` GROUP BY a.id,a.add_type,a.classify_id_first,a.classify_name_first,a.classify_id_second,a.classify_name_second,a.title,a.abstract,a.author,a.frequency,a.create_time,a.modify_time,a.state,a.publish_time,a.pre_publish_time,a.stage,a.msg_is_send,a.pre_msg_send,a.video_url,a.video_name,a.video_play_seconds,a.report_code,a.video_size,a.report_version,a.ths_msg_is_send,a.has_chapter,a.chapter_type,a.old_report_id,a.msg_send_time,a.admin_id,a.admin_real_name,a.approve_time,a.approve_id,a.detail_img_url,a.detail_pdf_url,a.last_modify_admin_id,a.last_modify_admin_name,a.content_modify_time,a.pv,a.uv,a.canvas_color,a.need_splice,a.head_resource_id,a.end_resource_id,a.classify_id_third,a.classify_name_third,a.collaborate_type,a.report_layout,a.is_public_publish,a.report_create_time,a.inherit_report_id,a.voice_generate_type,a.report_source
+
+// 		ORDER BY CASE a."state"
+//         WHEN 3 THEN 1
+//         WHEN 1 THEN 2
+//         WHEN 4 THEN 3
+//         WHEN 5 THEN 4
+//         WHEN 6 THEN 5
+//         ELSE 6
+//     END, a.modify_time DESC LIMIT ?,?`
+// 	pars = append(pars, startSize)
+// 	pars = append(pars, pageSize)
+// 	err = global.DmSQL["rddp"].Raw(sql, pars...).Find(&items).Error
+// 	return
+// }
+
 func GetReportListCount(condition string, pars []interface{}) (count int, err error) {
 	sql := `SELECT COUNT(1) AS count  FROM report WHERE 1=1 `
 	if condition != "" {
@@ -297,37 +438,35 @@ func DeleteReport(reportIds int) (err error) {
 }
 
 type ReportDetail struct {
-	Id                 int    `gorm:"column:id;primary_key;autoIncrement" description:"报告Id"`
-	AddType            int    `gorm:"column:add_type" description:"新增方式:1:新增报告,2:继承报告"`
-	ClassifyIdFirst    int    `gorm:"column:classify_id_first" description:"一级分类id"`
-	ClassifyNameFirst  string `gorm:"column:classify_name_first" description:"一级分类名称"`
-	ClassifyIdSecond   int    `gorm:"column:classify_id_second" description:"二级分类id"`
-	ClassifyNameSecond string `gorm:"column:classify_name_second" description:"二级分类名称"`
-	Title              string `gorm:"column:title" description:"标题"`
-	Abstract           string `gorm:"column:abstract" description:"摘要"`
-	Author             string `gorm:"column:author" description:"作者"`
-	Frequency          string `gorm:"column:frequency" description:"频度"`
-	CreateTime         string `gorm:"column:create_time" description:"创建时间"`
-	ModifyTime         string `gorm:"column:modify_time" description:"修改时间"`
-	State              int    `gorm:"column:state" description:"1:未发布,2:已发布"`
-	PublishTime        string `gorm:"column:publish_time" description:"发布时间"`
-	PrePublishTime     string `gorm:"column:pre_publish_time" description:"预发布时间"`
-	Stage              int    `gorm:"column:stage" description:"期数"`
-	MsgIsSend          int    `gorm:"column:msg_is_send" description:"消息是否已发送,0:否,1:是"`
-	PreMsgSend         int    `gorm:"column:pre_msg_send" description:"定时发布成功后是否立即推送模版消息:0否,1是"`
-	Content            string `gorm:"column:content" description:"内容"`
-	VideoUrl           string `gorm:"column:video_url" description:"音频文件URL"`
-	VideoName          string `gorm:"column:video_name" description:"音频文件名称"`
-	VideoPlaySeconds   string `gorm:"column:video_play_seconds" description:"音频播放时长"`
-	ContentSub         string `gorm:"column:content_sub" description:"内容前两个章节"`
-	ThsMsgIsSend       int    `gorm:"column:ths_msg_is_send" description:"客户群消息是否已发送,0:否,1:是"`
-	HasChapter         int    `gorm:"column:has_chapter" description:"是否有章节 0-否 1-是"`
-	ChapterType        string `gorm:"column:chapter_type" description:"章节类型 day-晨报 week-周报"`
-	AdminId            int    `gorm:"column:admin_id" description:"创建者账号"`
-	AdminRealName      string `gorm:"column:admin_real_name" description:"创建者姓名"`
-	ReportCode         string `gorm:"column:report_code" description:"报告唯一编码"`
-
-	// eta1.8.3(研报改版)相关内容
+	Id                  int    `gorm:"column:id;primary_key;autoIncrement" description:"报告Id"`
+	AddType             int    `gorm:"column:add_type" description:"新增方式:1:新增报告,2:继承报告"`
+	ClassifyIdFirst     int    `gorm:"column:classify_id_first" description:"一级分类id"`
+	ClassifyNameFirst   string `gorm:"column:classify_name_first" description:"一级分类名称"`
+	ClassifyIdSecond    int    `gorm:"column:classify_id_second" description:"二级分类id"`
+	ClassifyNameSecond  string `gorm:"column:classify_name_second" description:"二级分类名称"`
+	Title               string `gorm:"column:title" description:"标题"`
+	Abstract            string `gorm:"column:abstract" description:"摘要"`
+	Author              string `gorm:"column:author" description:"作者"`
+	Frequency           string `gorm:"column:frequency" description:"频度"`
+	CreateTime          string `gorm:"column:create_time" description:"创建时间"`
+	ModifyTime          string `gorm:"column:modify_time" description:"修改时间"`
+	State               int    `gorm:"column:state" description:"1:未发布,2:已发布"`
+	PublishTime         string `gorm:"column:publish_time" description:"发布时间"`
+	PrePublishTime      string `gorm:"column:pre_publish_time" description:"预发布时间"`
+	Stage               int    `gorm:"column:stage" description:"期数"`
+	MsgIsSend           int    `gorm:"column:msg_is_send" description:"消息是否已发送,0:否,1:是"`
+	PreMsgSend          int    `gorm:"column:pre_msg_send" description:"定时发布成功后是否立即推送模版消息:0否,1是"`
+	Content             string `gorm:"column:content" description:"内容"`
+	VideoUrl            string `gorm:"column:video_url" description:"音频文件URL"`
+	VideoName           string `gorm:"column:video_name" description:"音频文件名称"`
+	VideoPlaySeconds    string `gorm:"column:video_play_seconds" description:"音频播放时长"`
+	ContentSub          string `gorm:"column:content_sub" description:"内容前两个章节"`
+	ThsMsgIsSend        int    `gorm:"column:ths_msg_is_send" description:"客户群消息是否已发送,0:否,1:是"`
+	HasChapter          int    `gorm:"column:has_chapter" description:"是否有章节 0-否 1-是"`
+	ChapterType         string `gorm:"column:chapter_type" description:"章节类型 day-晨报 week-周报"`
+	AdminId             int    `gorm:"column:admin_id" description:"创建者账号"`
+	AdminRealName       string `gorm:"column:admin_real_name" description:"创建者姓名"`
+	ReportCode          string `gorm:"column:report_code" description:"报告唯一编码"`
 	ContentStruct       string `gorm:"column:content_struct" description:"内容组件"`
 	LastModifyAdminId   int    `gorm:"column:last_modify_admin_id" description:"最后更新人ID"`
 	LastModifyAdminName string `gorm:"column:last_modify_admin_name" description:"最后更新人姓名"`
@@ -348,6 +487,8 @@ type ReportDetail struct {
 	ReportLayout        int8   `gorm:"column:report_layout" description:"报告布局,1:常规布局,2:智能布局。默认:1"`
 	IsPublicPublish     int8   `gorm:"column:is_public_publish" description:"是否公开发布,1:是,2:否"`
 	ReportCreateTime    string `gorm:"column:report_create_time" description:"报告时间创建时间"`
+	ReportSource        int    `gorm:"column:report_source" description:"报告来源:1-系统内;2-智力共享"`
+	OutReportId         string `gorm:"column:out_report_id" description:"外部报告ID(或编码)"`
 }
 
 func GetReportById(reportId int) (item *ReportDetail, err error) {
@@ -443,7 +584,7 @@ func GetReportStage(classifyIdFirst, classifyIdSecond, classifyIdThird int) (cou
 
 type PublishReq struct {
 	ReportIds string `description:"报告id,多个用英文逗号隔开"`
-	ReportUrl string `description:"报告Url"`
+	//ReportUrl string `description:"报告Url"`
 }
 
 type PublishCancelReq struct {
@@ -661,6 +802,8 @@ type SaveReportContent struct {
 	NeedSplice     int    `description:"是否拼接版头版位的标记,主要是为了兼容历史报告。0-不需要 1-需要"`
 	HeadResourceId int    `description:"版头资源ID"`
 	EndResourceId  int    `description:"版尾资源ID"`
+
+	IsManualSave bool `description:"是否手动保存:true:手动 false:自动"`
 }
 
 func AddReportSaveLog(reportId, adminId int, content, contentSub, contentStruct, canvasColor, adminName string, headResourceId, endResourceId int) (err error) {
@@ -1121,3 +1264,23 @@ func GetReportFieldsByIds(ids []int, fields []string) (items []*Report, err erro
 	err = global.DmSQL["rddp"].Raw(sql, ids).Find(&items).Error
 	return
 }
+
+func (m *Report) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM report WHERE 1=1 %s`, condition)
+	err = global.DmSQL["rddp"].Raw(sql, pars...).Scan(&count).Error
+	return
+}
+
+func (m *Report) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*Report, err error) {
+	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 report WHERE 1=1 %s %s`, fields, condition, order)
+	err = global.DmSQL["rddp"].Raw(sql, pars...).Find(&items).Error
+	return
+}

+ 1 - 0
models/report_chapter.go

@@ -172,6 +172,7 @@ type EditReportChapterReq struct {
 	CanvasColor    string `description:"画布颜色"`
 	HeadResourceId int    `description:"版头资源ID"`
 	EndResourceId  int    `description:"版尾资源ID"`
+	IsManualSave   bool   `description:"是否手动保存:true:手动 false:自动"`
 }
 
 type EditTickList struct {

+ 86 - 0
models/report_history.go

@@ -0,0 +1,86 @@
+package models
+
+import (
+	"eta_gn/eta_api/global"
+	"fmt"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"time"
+)
+
+// ReportHistory 定义报告历史记录的结构体
+type ReportHistory struct {
+	Id              int       `gorm:"primaryKey"`
+	ReportId        int       `description:"报告id"`
+	ReportChapterId int       `description:"报告章节id"`
+	Title           string    `description:"标题"`
+	Content         string    `description:"内容"`
+	CreateTime      time.Time `description:"创建时间"`
+	ContentSub      string    `description:"部分内容"`
+	AdminId         int       `description:"创建人id"`
+	AdminName       string    `description:"创建人姓名"`
+	ContentStruct   string    `description:"内容组件"`
+	CanvasColor     string    `description:"画布颜色"`
+	HeadResourceId  int       `description:"版头资源ID"`
+	EndResourceId   int       `description:"版尾资源ID"`
+}
+
+func (r *ReportHistory) TableName() string {
+	return "report_history"
+}
+
+func (r *ReportHistory) Add() (err error) {
+	err = global.DmSQL["rddp"].Create(r).Error
+	return
+}
+
+func (r *ReportHistory) GetNoContentPageList(condition string, pars []interface{}, startSize, pageSize int) (list []*ReportHistory, err error) {
+	sql := fmt.Sprintf(`SELECT id, report_id, report_chapter_id, title, create_time, admin_id, admin_name  FROM %s WHERE 1=1 `, r.TableName())
+	if condition != "" {
+		sql += condition
+	}
+	sql += ` ORDER BY create_time DESC, id DESC LIMIT ?, ?`
+	pars = append(pars, startSize, pageSize)
+	err = global.DmSQL["rddp"].Raw(sql, pars...).Scan(&list).Error
+	return
+}
+
+func (r *ReportHistory) GetPageListCount(condition string, pars []interface{}) (count int, err error) {
+	sql := fmt.Sprintf(`SELECT COUNT(1) AS count  FROM %s WHERE 1=1 `, r.TableName())
+	if condition != "" {
+		sql += condition
+	}
+	err = global.DmSQL["rddp"].Raw(sql, pars...).Scan(&count).Error
+	return
+}
+
+// 删除接口
+func (r *ReportHistory) Delete() (err error) {
+	err = global.DmSQL["rddp"].Delete(r).Error
+	return
+}
+
+// 查询单条记录
+func (r *ReportHistory) GetById(id int) (item *ReportHistory, err error) {
+	err = global.DmSQL["rddp"].Where("id = ?", id).First(&item).Error
+	return
+}
+
+// ReportHistoryListItem 定义报告历史记录列表项的结构体
+type ReportHistoryListItem struct {
+	Id              int    `description:"报告历史记录id"`
+	ReportId        int    `description:"报告id"`
+	ReportChapterId int    `description:"报告章节id"`
+	Title           string `description:"标题"`
+	CreateTime      string `description:"创建时间"`
+	AdminId         int    `description:"创建人id"`
+	AdminName       string `description:"创建人姓名"`
+}
+
+type ReportHistoryListResp struct {
+	List   []*ReportHistoryListItem
+	Paging *paging.PagingItem `description:"分页数据"`
+}
+
+type DeleteReportHistoryReq struct {
+	Id int `description:"报告历史记录id"`
+}

+ 213 - 0
models/report_message.go

@@ -0,0 +1,213 @@
+package models
+
+import (
+	"eta_gn/eta_api/global"
+	"eta_gn/eta_api/utils"
+	"fmt"
+	"strings"
+	"time"
+
+	"github.com/rdlucklib/rdluck_tools/paging"
+)
+
+const (
+	ReportMessageTypeWriteNotice   = 1 // 撰写通知
+	ReportMessageTypeApprovePass   = 2 // 审批通过
+	ReportMessageTypeApproveRefuse = 3 // 审批驳回
+)
+
+type ReportMessage struct {
+	Id            int       `gorm:"primaryKey;column:id;type:int(10) unsigned;not null"`
+	SendUserId    int       `gorm:"column:send_user_id" description:"发送人ID"`
+	ReceiveUserId int       `gorm:"column:receive_user_id" description:"接受者ID"`
+	Content       string    `gorm:"column:content" description:"消息内容"`
+	Remark        string    `gorm:"column:remark" description:"备注信息"`
+	ExtraContent  string    `gorm:"column:extra_content" description:"额外信息-JSON"`
+	ReportType    int       `gorm:"column:report_type" description:"报告类型:1-研报;2-PPT"`
+	ReportId      int       `gorm:"column:report_id" description:"报告/PPT-ID"`
+	MessageType   int       `gorm:"column:message_type" description:"消息类型:1-撰写通知;2-审批通过;3-审批驳回"`
+	IsRead        int       `gorm:"column:is_read" description:"是否已读:0-未读;1-已读"`
+	CreateTime    time.Time `gorm:"column:create_time" description:"消息时间"`
+	ModifyTime    time.Time `gorm:"column:modify_time" description:"更新时间"`
+}
+
+var ReportMessageCols = struct {
+	Id            string
+	SendUserId    string
+	ReceiveUserId string
+	Content       string
+	Remark        string
+	ExtraContent  string
+	ReportType    string
+	ReportId      string
+	MessageType   string
+	IsRead        string
+	CreateTime    string
+}{
+	Id:            "id",
+	SendUserId:    "send_user_id",
+	ReceiveUserId: "receive_user_id",
+	Content:       "content",
+	Remark:        "remark",
+	ExtraContent:  "extra_content",
+	ReportType:    "report_type",
+	ReportId:      "report_id",
+	MessageType:   "message_type",
+	IsRead:        "is_read",
+	CreateTime:    "create_time",
+}
+
+func (m *ReportMessage) TableName() string {
+	return "report_message"
+}
+
+func (m *ReportMessage) PrimaryId() string {
+	return ReportMessageCols.Id
+}
+
+func (m *ReportMessage) Create() (err error) {
+	err = global.DmSQL["rddp"].Create(m).Error
+	return
+}
+
+func (m *ReportMessage) CreateMulti(items []*ReportMessage) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	err = global.DmSQL["rddp"].CreateInBatches(items, utils.MultiAddNum).Error
+	return
+}
+
+func (m *ReportMessage) Update(cols []string) (err error) {
+	err = global.DmSQL["rddp"].Select(cols).Updates(m).Error
+	return
+}
+
+func (m *ReportMessage) Del() (err error) {
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	err = global.DmSQL["rddp"].Exec(sql, m.Id).Error
+	return
+}
+
+func (m *ReportMessage) MultiDel(menuIds []int) (err error) {
+	if len(menuIds) == 0 {
+		return
+	}
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s IN (%s)`, m.TableName(), m.PrimaryId(), utils.GetOrmInReplace(len(menuIds)))
+	err = global.DmSQL["rddp"].Exec(sql, menuIds).Error
+	return
+}
+
+func (m *ReportMessage) GetItemById(id int) (item *ReportMessage, err error) {
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	err = global.DmSQL["rddp"].Raw(sql, id).First(&item).Error
+	return
+}
+
+func (m *ReportMessage) GetItemByCondition(condition string, pars []interface{}, orderRule string) (item *ReportMessage, err error) {
+	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 = global.DmSQL["rddp"].Raw(sql, pars...).First(&item).Error
+	return
+}
+
+func (m *ReportMessage) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM %s WHERE 1=1 %s`, m.TableName(), condition)
+	err = global.DmSQL["rddp"].Raw(sql, pars...).Scan(&count).Error
+	return
+}
+
+func (m *ReportMessage) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*ReportMessage, err error) {
+	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 = global.DmSQL["rddp"].Raw(sql, pars...).Find(&items).Error
+	return
+}
+
+func (m *ReportMessage) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*ReportMessage, err error) {
+	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)
+	pars = append(pars, startSize)
+	pars = append(pars, pageSize)
+	err = global.DmSQL["rddp"].Raw(sql, pars...).Find(&items).Error
+	return
+}
+
+// ReportMessageItem 报告消息
+type ReportMessageItem struct {
+	Id            int
+	SendUserId    int                       `description:"发送人ID"`
+	ReceiveUserId int                       `description:"接收者ID"`
+	Content       string                    `description:"消息内容"`
+	Remark        string                    `description:"备注信息"`
+	ReportType    int                       `description:"报告类型:1-研报;2-PPT"`
+	ReportId      int                       `description:"报告/PPT-ID"`
+	ReportState   int                       `description:"报告当前状态"`
+	MessageType   int                       `description:"消息类型:1-撰写通知;2-审批通过;3-审批驳回"`
+	IsRead        int                       `description:"是否已读:0-未读;1-已读"`
+	CreateTime    string                    `description:"消息时间"`
+	ApproveMsg    *ReportMessageApproveItem `description:"审批信息"`
+}
+
+// ReportMessageApproveItem 报告消息审批内容
+type ReportMessageApproveItem struct {
+	Title           string `description:"报告标题"`
+	ApproveType     int    `description:"审批类型:1-通过;2-驳回"`
+	ApproveUserId   int    `description:"审批人ID"`
+	ApproveUserName string `description:"审批人"`
+	ApproveRemark   string `description:"审批备注(驳回意见)"`
+}
+
+// FormatReportMessage2Item 格式化报告消息
+func FormatReportMessage2Item(origin *ReportMessage) (item *ReportMessageItem) {
+	item = new(ReportMessageItem)
+	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.ReportType = origin.ReportType
+	item.ReportId = origin.ReportId
+	item.MessageType = origin.MessageType
+	item.IsRead = origin.IsRead
+	item.CreateTime = utils.TimeTransferString(utils.FormatDateTime, origin.CreateTime)
+	return
+}
+
+// ReportMessageListReq 消息列表请求参数
+type ReportMessageListReq struct {
+	PageSize     int `form:"PageSize"`
+	CurrentIndex int `form:"CurrentIndex"`
+}
+
+// ReportMessageListResp 消息列表响应体
+type ReportMessageListResp struct {
+	List        []*ReportMessageItem
+	Paging      *paging.PagingItem `description:"分页数据"`
+	UnreadTotal int                `description:"消息未读数"`
+}
+
+// ReportMessageReadReq 消息已读请求体
+type ReportMessageReadReq struct {
+	MessageId int `description:"消息ID"`
+}

+ 7 - 0
models/report_v2.go

@@ -432,3 +432,10 @@ type EditLayoutImgReq struct {
 	HeadResourceId int    `description:"版头资源ID"`
 	EndResourceId  int    `description:"版尾资源ID"`
 }
+
+// MoveReportClassify 转移报告分类
+func MoveReportClassify(firstId, secondId, thirdId int, firstName, secondName, thirdName string, originFirst, originSecond int) (err error) {
+	sql := `UPDATE report SET classify_id_first = ?, classify_id_second = ?, classify_id_third = ?, classify_name_first = ?, classify_name_second = ?, classify_name_third = ? WHERE classify_id_first = ? AND classify_id_second = ?`
+	err = global.DmSQL["rddp"].Exec(sql, firstId, secondId, thirdId, firstName, secondName, thirdName, originFirst, originSecond).Error
+	return
+}

+ 9 - 0
models/system/response/sys_role_admin.go

@@ -40,3 +40,12 @@ type EnglishAuthRoleDetailResp struct {
 	RoleTypeCode string `description:"角色类型编码"`
 	AuthOk       bool   `description:"是否有权限"`
 }
+
+type AdminListResp struct {
+	List []*AdminItem
+}
+type AdminItem struct {
+	AdminId   int
+	AdminName string `description:"系统用户名称"`
+	RealName  string `description:"系统用户姓名"`
+}

+ 7 - 0
models/system/sys_admin.go

@@ -354,3 +354,10 @@ func GetSysAdminList(condition string, pars []interface{}, fieldArr []string, or
 	err = global.DEFAULT_DmSQL.Raw(sql, pars...).Find(&items).Error
 	return
 }
+
+// GetSysAdminListAll 获取所有admin列表
+func GetSysAdminListAll() (items []*Admin, err error) {
+	sql := `SELECT admin_id, admin_name, real_name  FROM "admin" `
+	err = global.DEFAULT_DmSQL.Raw(sql).Find(&items).Error
+	return
+}

+ 171 - 0
routers/commentsRouter.go

@@ -6487,6 +6487,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta_gn/eta_api/controllers:ClassifyController"] = append(beego.GlobalControllerRouter["eta_gn/eta_api/controllers:ClassifyController"],
+        beego.ControllerComments{
+            Method: "AdminList",
+            Router: `/user/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta_gn/eta_api/controllers:CompanyPermissionController"] = append(beego.GlobalControllerRouter["eta_gn/eta_api/controllers:CompanyPermissionController"],
         beego.ControllerComments{
             Method: "List",
@@ -6514,6 +6523,42 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta_gn/eta_api/controllers:ImageConfController"] = append(beego.GlobalControllerRouter["eta_gn/eta_api/controllers:ImageConfController"],
+        beego.ControllerComments{
+            Method: "AddImageMaterial",
+            Router: `/add/image/material`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta_gn/eta_api/controllers:ImageConfController"] = append(beego.GlobalControllerRouter["eta_gn/eta_api/controllers:ImageConfController"],
+        beego.ControllerComments{
+            Method: "DeleteImageMaterial",
+            Router: `/delete/image/material`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta_gn/eta_api/controllers:ImageConfController"] = append(beego.GlobalControllerRouter["eta_gn/eta_api/controllers:ImageConfController"],
+        beego.ControllerComments{
+            Method: "EditImageMaterial",
+            Router: `/edit/image/material`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta_gn/eta_api/controllers:ImageConfController"] = append(beego.GlobalControllerRouter["eta_gn/eta_api/controllers:ImageConfController"],
+        beego.ControllerComments{
+            Method: "GetImageMaterial",
+            Router: `/get/image/material`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta_gn/eta_api/controllers:MeetingProbabilitiesController"] = append(beego.GlobalControllerRouter["eta_gn/eta_api/controllers:MeetingProbabilitiesController"],
         beego.ControllerComments{
             Method: "Detail",
@@ -6694,6 +6739,42 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta_gn/eta_api/controllers:PptV2Controller"] = append(beego.GlobalControllerRouter["eta_gn/eta_api/controllers:PptV2Controller"],
+        beego.ControllerComments{
+            Method: "CreateReport",
+            Router: `/report/add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta_gn/eta_api/controllers:PptV2Controller"] = append(beego.GlobalControllerRouter["eta_gn/eta_api/controllers:PptV2Controller"],
+        beego.ControllerComments{
+            Method: "AuthList",
+            Router: `/report/auth_list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta_gn/eta_api/controllers:PptV2Controller"] = append(beego.GlobalControllerRouter["eta_gn/eta_api/controllers:PptV2Controller"],
+        beego.ControllerComments{
+            Method: "ReportClassify",
+            Router: `/report/classify`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta_gn/eta_api/controllers:PptV2Controller"] = append(beego.GlobalControllerRouter["eta_gn/eta_api/controllers:PptV2Controller"],
+        beego.ControllerComments{
+            Method: "ReportList",
+            Router: `/report/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta_gn/eta_api/controllers:PptV2Controller"] = append(beego.GlobalControllerRouter["eta_gn/eta_api/controllers:PptV2Controller"],
         beego.ControllerComments{
             Method: "SaveLog",
@@ -6865,6 +6946,42 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta_gn/eta_api/controllers:PptV2HistoryController"] = append(beego.GlobalControllerRouter["eta_gn/eta_api/controllers:PptV2HistoryController"],
+        beego.ControllerComments{
+            Method: "Delete",
+            Router: `/del`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta_gn/eta_api/controllers:PptV2HistoryController"] = append(beego.GlobalControllerRouter["eta_gn/eta_api/controllers:PptV2HistoryController"],
+        beego.ControllerComments{
+            Method: "Detail",
+            Router: `/detail`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta_gn/eta_api/controllers:PptV2HistoryController"] = append(beego.GlobalControllerRouter["eta_gn/eta_api/controllers:PptV2HistoryController"],
+        beego.ControllerComments{
+            Method: "List",
+            Router: `/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta_gn/eta_api/controllers:PptV2HistoryController"] = append(beego.GlobalControllerRouter["eta_gn/eta_api/controllers:PptV2HistoryController"],
+        beego.ControllerComments{
+            Method: "Revert",
+            Router: `/revert`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta_gn/eta_api/controllers:ReportAuthorController"] = append(beego.GlobalControllerRouter["eta_gn/eta_api/controllers:ReportAuthorController"],
         beego.ControllerComments{
             Method: "Author",
@@ -7297,6 +7414,24 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta_gn/eta_api/controllers:ReportController"] = append(beego.GlobalControllerRouter["eta_gn/eta_api/controllers:ReportController"],
+        beego.ControllerComments{
+            Method: "MessageList",
+            Router: `/message/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta_gn/eta_api/controllers:ReportController"] = append(beego.GlobalControllerRouter["eta_gn/eta_api/controllers:ReportController"],
+        beego.ControllerComments{
+            Method: "MessageRead",
+            Router: `/message/read`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta_gn/eta_api/controllers:ReportController"] = append(beego.GlobalControllerRouter["eta_gn/eta_api/controllers:ReportController"],
         beego.ControllerComments{
             Method: "PrePublishReport",
@@ -7396,6 +7531,42 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta_gn/eta_api/controllers:ReportHistoryController"] = append(beego.GlobalControllerRouter["eta_gn/eta_api/controllers:ReportHistoryController"],
+        beego.ControllerComments{
+            Method: "Delete",
+            Router: `/del`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta_gn/eta_api/controllers:ReportHistoryController"] = append(beego.GlobalControllerRouter["eta_gn/eta_api/controllers:ReportHistoryController"],
+        beego.ControllerComments{
+            Method: "Detail",
+            Router: `/detail`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta_gn/eta_api/controllers:ReportHistoryController"] = append(beego.GlobalControllerRouter["eta_gn/eta_api/controllers:ReportHistoryController"],
+        beego.ControllerComments{
+            Method: "List",
+            Router: `/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta_gn/eta_api/controllers:ReportHistoryController"] = append(beego.GlobalControllerRouter["eta_gn/eta_api/controllers:ReportHistoryController"],
+        beego.ControllerComments{
+            Method: "Revert",
+            Router: `/revert`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta_gn/eta_api/controllers:ReportUploadCommonController"] = append(beego.GlobalControllerRouter["eta_gn/eta_api/controllers:ReportUploadCommonController"],
         beego.ControllerComments{
             Method: "UploadImg",

+ 15 - 0
routers/router.go

@@ -344,6 +344,21 @@ func init() {
 				&controllers.BIDaShboardController{},
 			),
 		),
+		web.NSNamespace("/report_history",
+			web.NSInclude(
+				&controllers.ReportHistoryController{},
+			),
+		),
+		web.NSNamespace("/ppt_history",
+			web.NSInclude(
+				&controllers.PptV2HistoryController{},
+			),
+		),
+		web.NSNamespace("/image_conf",
+			web.NSInclude(
+				&controllers.ImageConfController{},
+			),
+		),
 	)
 	web.AddNamespace(ns)
 }

+ 145 - 17
services/classify.go

@@ -7,6 +7,7 @@ import (
 	"eta_gn/eta_api/utils"
 	"fmt"
 	"sort"
+	"strconv"
 	"time"
 )
 
@@ -187,11 +188,12 @@ func moveReportClassify(classifyInfo, prevClassify, nextClassify *models.Classif
 // @datetime 2024-06-17 11:01:21
 // @param classifyName string
 // @param parentId int
-// @param chartPermissionIdList []int
+// @param isRemind int
+// @param remindTime string
 // @return err error
 // @return errMsg string
 // @return isSendEmail bool
-func AddReportClassify(classifyName string, parentId int, chartPermissionIdList []int) (err error, errMsg string, isSendEmail bool) {
+func AddReportClassify(classifyName string, parentId int, classifyType, isRemind int, remindTime string, visibleUserIds []int) (err error, errMsg string, isSendEmail bool) {
 	isSendEmail = true
 	errMsg = `添加失败`
 	item, err := models.GetClassifyByName(classifyName, parentId)
@@ -217,6 +219,7 @@ func AddReportClassify(classifyName string, parentId int, chartPermissionIdList
 	// 父级分类下的子分类数量
 	var childClassifyCount int
 
+	var levelPath string
 	if parentId > 0 {
 		// 获取父级分类信息
 		parentClassifyItem, err = models.GetClassifyById(parentId)
@@ -228,6 +231,7 @@ func AddReportClassify(classifyName string, parentId int, chartPermissionIdList
 			return
 		}
 		level = parentClassifyItem.Level + 1
+		levelPath = parentClassifyItem.LevelPath // 这里只是一半,新增后后面跟上新分类ID
 
 		if level > 3 {
 			errMsg = "分类层级不可超过三级"
@@ -236,10 +240,10 @@ func AddReportClassify(classifyName string, parentId int, chartPermissionIdList
 		}
 
 		// 判断是否分类存在待操作的审批单
-		err, errMsg = checkClassifyApprove(parentClassifyItem)
-		if err != nil {
-			return
-		}
+		//err, errMsg = checkClassifyApprove(parentClassifyItem)
+		//if err != nil {
+		//	return
+		//}
 
 		// 获取父级分类下的子分类数量
 		childClassifyCount, err = models.GetCountClassifyChildByParentId(parentId)
@@ -269,11 +273,14 @@ func AddReportClassify(classifyName string, parentId int, chartPermissionIdList
 	classify.ReportDetailShowType = 1 //默认列表格式
 	classify.IsShow = 1
 	classify.Level = level
-
-	err = models.AddClassify(classify)
+	classify.ClassifyType = classifyType
+	classify.IsRemind = isRemind
+	classify.RemindTime = remindTime
+	err = models.AddClassify(classify, parentId, visibleUserIds)
 	if err != nil {
 		return
 	}
+
 	// 如果父级分类不为空的话,那么就标记有子级分类,同时
 	if parentClassifyItem != nil {
 		parentClassifyItem.HasChild = 1
@@ -288,15 +295,46 @@ func AddReportClassify(classifyName string, parentId int, chartPermissionIdList
 			}
 
 			// 继承父级分类审批流
-			go inheritReportApproveFlow(parentClassifyItem, classify)
-
+			//go inheritReportApproveFlow(parentClassifyItem, classify)
 			moveReportByAddClassify(parentClassifyItem, classify)
+			// err = models.UpdateClassifyVisible(classify.Id, visibleUserIds)
+			// if err != nil {
+			// 	errMsg = "添加分类可见权限失败"
+			// 	return
+			// }
 		}
 	}
 
+	// 更新分类的LevelPath
+	if parentId > 0 {
+		levelPath = fmt.Sprintf("%s,%d", levelPath, classify.Id)
+	}
+	if parentId == 0 {
+		levelPath = strconv.Itoa(classify.Id)
+	}
+	classify.LevelPath = levelPath
+	if e := classify.UpdateClassify([]string{"LevelPath"}); e != nil {
+		err = fmt.Errorf("更新分类层级路径失败, %v", e)
+		return
+	}
 	return
 }
 
+// inheritParentClassifyVisibleUser 继承父类的可见用户
+// func inheritParentClassifyVisibleUser(parentClassifyId, currClassifyId int) (err error) {
+// 	parentUserIds, err := models.GetClassifyVisibleUserIdByClassifyId(parentClassifyId)
+// 	if err != nil {
+// 		return
+// 	}
+// 	currUserIds, err := models.GetClassifyVisibleUserIdByClassifyId(currClassifyId)
+// 	if err != nil {
+// 		return
+// 	}
+// 	parentUserIds = append(parentUserIds, currUserIds...)
+// 	err = models.ExtendClassifyVisible(parentClassifyId, currClassifyId, parentUserIds)
+// 	return
+// }
+
 // checkClassifyApprove
 // @Description: 判断分类是否存在待操作的审批单
 // @author: Roc
@@ -382,16 +420,26 @@ func moveReportByAddClassify(parentClassifyInfo, currClassifyInfo *models.Classi
 	}
 
 	// 报告的分类归属调整,转为下一级的分类
-
+	var classifyIdFirst, classifyIdSecond, classifyIdThird, originIdFirst, originIdSecond int
 	var condition, updateStr string
 	pars := make([]interface{}, 0)
 	switch currClassifyInfo.Level {
 	case 3: // 当前分类是3级分类
 		updateStr += ` classify_id_third = ?,classify_name_third = ?`
 		condition += ` AND classify_id_second = ? `
+		classifyIdFirst = parentClassifyInfo.ParentId
+		classifyIdSecond = parentClassifyInfo.Id
+		classifyIdThird = currClassifyInfo.Id
+		originIdFirst = classifyIdFirst
+		originIdSecond = classifyIdSecond
 	case 2: // 当前分类是2级分类
 		updateStr += ` classify_id_second = ?,classify_name_second = ?`
 		condition += ` AND classify_id_first = ? `
+		classifyIdFirst = parentClassifyInfo.Id
+		classifyIdSecond = currClassifyInfo.Id
+		classifyIdThird = 0
+		originIdFirst = classifyIdFirst
+		originIdSecond = 0
 	default:
 		err = errors.New("错误的分类层级")
 		return
@@ -416,6 +464,34 @@ func moveReportByAddClassify(parentClassifyInfo, currClassifyInfo *models.Classi
 		return
 	}
 
+	// 分类ID-NAME
+	classifyOb := new(models.Classify)
+	classifies, e := classifyOb.GetItemsByCondition(``, make([]interface{}, 0), []string{}, "")
+	if e != nil {
+		err = fmt.Errorf("获取分类列表失败, %v", e)
+		return
+	}
+	classifyIdName := make(map[int]string)
+	for _, v := range classifies {
+		classifyIdName[v.Id] = v.ClassifyName
+	}
+
+	// 转移上级分类下的所有报告至该分类,区分研报和PPT
+	if currClassifyInfo.ClassifyType == utils.ReportTypeDefault {
+		if e := models.MoveReportClassify(classifyIdFirst, classifyIdSecond, classifyIdThird, classifyIdName[classifyIdFirst], classifyIdName[classifyIdSecond], classifyIdName[classifyIdThird], originIdFirst, originIdSecond); e != nil {
+			err = fmt.Errorf("转移研报分类失败, %v", e)
+			return
+		}
+	}
+	if currClassifyInfo.ClassifyType == utils.ReportTypePPT {
+		if e := models.MovePptReportClassify(currClassifyInfo.Id, parentClassifyInfo.Id); e != nil {
+			err = fmt.Errorf("转移PPT研报分类失败, %v", e)
+			return
+		}
+	}
+
+	// 更新分类报告计数
+	go UpdateClassifyReportNum(currClassifyInfo.Id)
 	return
 }
 
@@ -597,11 +673,12 @@ func inheritReportApproveFlow(parentClassifyItem, currClassifyItem *models.Class
 // @datetime 2024-06-17 13:31:33
 // @param classifyId int
 // @param classifyName string
-// @param chartPermissionIdList []int
+// @param isRemind int
+// @param remindTime string
 // @return err error
 // @return errMsg string
 // @return isSendEmail bool
-func EditReportClassify(classifyId int, classifyName string, chartPermissionIdList []int) (err error, errMsg string, isSendEmail bool) {
+func EditReportClassify(classifyId int, classifyName string, isRemind int, remindTime string, visibleUserIds []int) (err error, errMsg string, isSendEmail bool) {
 	isSendEmail = true
 	errMsg = `修改失败`
 
@@ -616,6 +693,20 @@ func EditReportClassify(classifyId int, classifyName string, chartPermissionIdLi
 	}
 	//originName := item.ClassifyName
 
+	// 分类层级路径
+	if item.ParentId == 0 {
+		item.LevelPath = strconv.Itoa(item.Id)
+	}
+	if item.ParentId > 0 {
+		classifyParent, e := models.GetClassifyById(item.ParentId)
+		if e != nil {
+			errMsg = "父级分类有误"
+			err = fmt.Errorf("获取父级分类失败, %v", e)
+			return
+		}
+		item.LevelPath = fmt.Sprintf("%s,%d", classifyParent.LevelPath, item.Id)
+	}
+
 	// 重名校验
 	existName, e := models.GetClassifyByName(classifyName, item.ParentId)
 	if e != nil && !utils.IsErrNoRow(e) {
@@ -630,16 +721,21 @@ func EditReportClassify(classifyId int, classifyName string, chartPermissionIdLi
 		return
 	}
 	item.ClassifyName = classifyName
-
-	// ETA1.8.3:不允许修改上级分类  2024-6-17 13:21:01
-	//item.ParentId = req.ParentId
+	item.IsRemind = isRemind
+	item.RemindTime = remindTime
 	item.ModifyTime = time.Now().Local()
+
 	cols := make([]string, 0)
-	cols = append(cols, "ClassifyName", "ModifyTime")
+	cols = append(cols, "ClassifyName", "IsRemind", "RemindTime", "ModifyTime", "LevelPath")
 	err = item.UpdateClassify(cols)
 	if err != nil {
 		return
 	}
+	err = models.UpdateClassifyVisible(item.Id, visibleUserIds)
+	if err != nil {
+		errMsg = "更新可见权限失败"
+		return
+	}
 	return
 }
 
@@ -718,6 +814,22 @@ func GetClassifyListTreeRecursive(list []*models.ClassifyList, parentId int) []*
 	return res
 }
 
+// RecursiveFilterNoChildTreeClassify 递归过滤没有子分类的分类
+func RecursiveFilterNoChildTreeClassify(list []*models.ClassifyList) []*models.ClassifyList {
+	res := make([]*models.ClassifyList, 0)
+	for _, v := range list {
+		v.Child = RecursiveFilterNoChildTreeClassify(v.Child)
+		if len(v.Child) == 0 && v.HasChild == 1 {
+			continue
+		}
+		if len(v.Child) == 0 {
+			v.Child = nil
+		}
+		res = append(res, v)
+	}
+	return res
+}
+
 // BySortAndCreateTime 用来排序,先按Sort字段升序排序,若Sort相同,则按照CreateTime字段升序排序。
 type BySortAndCreateTime []*models.ClassifyList
 
@@ -740,3 +852,19 @@ func (a BySortAndCreateTime) Less(i, j int) bool {
 func SortClassifyListBySortAndCreateTime(classifyList []*models.ClassifyList) {
 	sort.Sort(BySortAndCreateTime(classifyList))
 }
+
+// GetClassifyChildIdsTreeRecursive 递归获取分类所有子分类ID
+func GetClassifyChildIdsTreeRecursive(list []*models.Classify, parentId int) []int {
+	var res []int
+	for _, v := range list {
+		if v.ParentId == parentId {
+			res = append(res, v.Id)
+			// 子分类的子类
+			childIds := GetClassifyChildIdsTreeRecursive(list, v.Id)
+			if len(childIds) > 0 {
+				res = append(res, childIds...)
+			}
+		}
+	}
+	return res
+}

+ 17 - 0
services/data/chart_info_elastic.go

@@ -9,6 +9,23 @@ import (
 	"strings"
 )
 
+// AddAllChartInfo
+// @Description: 更新es中的所有的图表数据
+// @author: Roc
+// @datetime 2024-11-20 13:11:35
+func AddAllChartInfo() {
+	allList, err := data_manage.GetChartInfoAllList()
+	if err != nil {
+		fmt.Println("GetArticleAll Err:", err.Error())
+		return
+	}
+	total := len(allList)
+	for k, v := range allList {
+		EsAddOrEditChartInfo(v.ChartInfoId)
+		fmt.Println("剩余", total-k-1, "条图表数据,当前图表id:", v.ChartInfoId)
+	}
+}
+
 // EsAddOrEditChartInfo 新增和修改ES中的图表数据
 func EsAddOrEditChartInfo(chartInfoId int) {
 	var err error

+ 10 - 0
services/data/edb_info.go

@@ -706,6 +706,16 @@ func AddOrEditEdbInfoToEs(edbInfoId int) {
 	go elastic.EsAddOrEditEdbInfoData(utils.DATA_INDEX_NAME, strconv.Itoa(itemInfo.EdbInfoId), itemInfo)
 }
 
+// AddOrEditAllEdbInfoToEs 修复ES中的所有指标
+func AddOrEditAllEdbInfoToEs() {
+	//添加es
+	total, itemInfoList, _ := data_manage.GetEdbInfoFilterList("", []interface{}{}, 0, 100000)
+	for k, itemInfo := range itemInfoList {
+		elastic.EsAddOrEditEdbInfoData(utils.DATA_INDEX_NAME, strconv.Itoa(itemInfo.EdbInfoId), itemInfo)
+		fmt.Println("剩余", int(total)-k-1, "条指标数据")
+	}
+}
+
 // DeleteEdbInfoToEs 删除ES中的指标
 func DeleteEdbInfoToEs(edbInfoId int) {
 	//添加es

+ 5 - 0
services/data/excel/excel_info.go

@@ -980,6 +980,11 @@ func GetExcelRuleList(excelInfoId int) (resp *response.ExcelRuleListResp, err er
 	if err != nil {
 		return
 	}
+	for _, v := range excelInfoList {
+		v.BackgroundColor = strings.TrimSpace(v.BackgroundColor)
+		v.FontColor = strings.TrimSpace(v.FontColor)
+		resp.List = append(resp.List, v)
+	}
 	resp.List = excelInfoList
 	return
 }

+ 34 - 0
services/data/excel/mixed_table.go

@@ -466,11 +466,45 @@ func GetMixedTableCellData(mixedTableReq request.MixedTableReq, lang string) (ne
 		return
 	}
 
+	config = setDefaultCellSize(config)
+
 	newMixedTableCellDataList = config
 
 	return
 }
 
+func setDefaultCellSize(config [][]request.MixedTableCellDataReq) (newConfig [][]request.MixedTableCellDataReq) {
+	newConfig = config
+	for i, row := range config {
+		for j, cell := range row {
+			var styleConf request.MixCellShowStyle
+			if cell.ShowStyle == `` {
+				styleConf = request.MixCellShowStyle{
+					Width:  140,
+					Height: 35,
+				}
+			} else {
+				err := json.Unmarshal([]byte(cell.ShowStyle), &styleConf)
+				if err != nil {
+					continue
+				}
+				if styleConf.Width == 0 {
+					styleConf.Width = 140
+				}
+				if styleConf.Height == 0 {
+					styleConf.Height = 35
+				}
+			}
+			tmpStyleConf, err := json.Marshal(styleConf)
+			if err == nil {
+				cell.ShowStyle = string(tmpStyleConf)
+			}
+			newConfig[i][j] = cell
+		}
+	}
+	return
+}
+
 // getCalculateValue 获取公式计算的结果
 func getCalculateValueByCell(calculateCellMap map[string]Cell, key string, cellKeyValMap map[string]float64) (val float64, has bool, err error, errMsg string) {
 	// 单元格的标签名

+ 13 - 34
services/data/predict_edb_info.go

@@ -754,50 +754,29 @@ func GetPredictCalculateDataListByPredictEdbInfo(edbInfo *data_manage.EdbInfo, s
 
 // ModifyPredictEdbBaseInfoBySourceEdb  根据来源ETA指标修改预测指标的基础信息
 func ModifyPredictEdbBaseInfoBySourceEdb(sourceEDdbInfo *data_manage.EdbInfo, frequency, unit string) {
-	list, err := data_manage.GetGroupPredictEdbBySourceEdbInfoId(sourceEDdbInfo.EdbInfoId)
+	idList, err := data_manage.GetGroupPredictEdbInfoIdListBySourceEdbInfoId(sourceEDdbInfo.EdbInfoId)
 	if err != nil {
 		return
 	}
-	for _, v := range list {
-		v.Frequency = frequency
-		v.Unit = unit
-		v.Update([]string{"Frequency", "Unit"})
-		AddOrEditEdbInfoToEs(v.EdbInfoId)
+
+	obj := data_manage.EdbInfo{}
+	for _, v := range idList {
+		updateMap := map[string]interface{}{"frequency": frequency, "unit": unit}
+		_ = obj.UpdateById(v, updateMap)
+		AddOrEditEdbInfoToEs(v)
 	}
 }
 
 // ModifyPredictEdbEnBaseInfoBySourceEdb  根据来源ETA指标修改预测指标的英文基础信息
 func ModifyPredictEdbEnBaseInfoBySourceEdb(sourceEDdbInfo *data_manage.EdbInfo, unitEn string) {
-	list, err := data_manage.GetGroupPredictEdbBySourceEdbInfoId(sourceEDdbInfo.EdbInfoId)
+	idList, err := data_manage.GetGroupPredictEdbInfoIdListBySourceEdbInfoId(sourceEDdbInfo.EdbInfoId)
 	if err != nil {
 		return
 	}
-	for _, v := range list {
-		v.UnitEn = unitEn
-		v.Update([]string{"UnitEn"})
-		AddOrEditEdbInfoToEs(v.EdbInfoId)
+	obj := data_manage.EdbInfo{}
+	for _, v := range idList {
+		updateMap := map[string]interface{}{"unit_en": unitEn}
+		_ = obj.UpdateById(v, updateMap)
+		AddOrEditEdbInfoToEs(v)
 	}
 }
-
-// ModifyPredictEdbUnitBySourceEdbInfoId
-// @Description: 根据来源ETA指标修改预测指标的频度和单位基础信息
-// @author: Roc
-// @datetime 2024-01-05 11:07:39
-// @param sourceEdbInfoId int
-// @param frequency string
-// @param unit string
-// @return err error
-func ModifyPredictEdbUnitBySourceEdbInfoId(sourceEdbInfoId int, frequency, unit string) (err error) {
-	list, err := data_manage.GetGroupPredictEdbBySourceEdbInfoId(sourceEdbInfoId)
-	if err != nil {
-		return
-	}
-
-	for _, v := range list {
-		v.Frequency = frequency
-		v.Unit = unit
-		v.Update([]string{"Frequency", "Unit"})
-		AddOrEditEdbInfoToEs(v.EdbInfoId)
-	}
-	return
-}

+ 1 - 0
services/data/range_analysis/chart_info.go

@@ -644,6 +644,7 @@ func GetChartDataByEdbInfoListBySeries(chartInfoId int, dateType, startYear int,
 			edbInfoMappingList[k].MaxData = maxData
 		}
 	}
+
 	dataResp = data_manage.ChartRangeAnalysisDataResp{ChartRangeAnalysisExtraConf: req, SeriesId: seriesMappingItem.FactorEdbSeriesId}
 	// 查询配置关联关系
 	if req.MultipleGraphConfigId > 0 {

+ 36 - 6
services/excel/lucky_sheet.go

@@ -3,6 +3,7 @@ package excel
 import (
 	"encoding/json"
 	"errors"
+	"eta_gn/eta_api/models/data_manage/excel"
 	"eta_gn/eta_api/models/data_manage/excel/request"
 	"eta_gn/eta_api/utils"
 	"fmt"
@@ -1719,15 +1720,25 @@ func GetTableDataByCustomData(excelType int, data request.TableDataReq, lang str
 }
 
 // GetTableDataByMixedTableData 通过混合表格数据获取表格数据
-func GetTableDataByMixedTableData(config [][]request.MixedTableCellDataReq, hideMerged bool) (selfTableData TableData, err error) {
+func GetTableDataByMixedTableData(config [][]request.MixedTableCellDataReq, hideMerged bool, excelInfoId int) (selfTableData TableData, err error) {
 	tableDataList := make([][]LuckySheetDataValue, 0)
 	mergeList := make([]TableDataMerge, 0)
 
+	excelRuleMappingList, err := excel.GetExcelRuleMappingByExcelInfoId(excelInfoId)
+	if err != nil {
+		return
+	}
+	excelRuleMap := make(map[int]*excel.ExcelInfoRuleMappingView)
+	for _, v := range excelRuleMappingList {
+		excelRuleMap[v.ExcelInfoRuleMappingId] = v
+	}
+	ruleScopeMap := generateRuleScopeIndexMap(excelRuleMappingList)
+
 	// 开始文本行了
 	{
-		for _, row := range config {
+		for i, row := range config {
 			dataCol := make([]LuckySheetDataValue, 0)
-			for _, cell := range row {
+			for j, cell := range row {
 				tmp := LuckySheetDataValue{
 					Value:     cell.Value,
 					Monitor:   cell.ShowValue,
@@ -1751,10 +1762,21 @@ func GetTableDataByMixedTableData(config [][]request.MixedTableCellDataReq, hide
 					}
 					showFormatValue := fmt.Sprintf("%v", cell.ShowFormatValue)
 					if styleConfig.BackgroundColor != "" {
-						tmp.Background = styleConfig.BackgroundColor
+						tmp.Background = strings.TrimSpace(styleConfig.BackgroundColor)
 					}
 					if styleConfig.Color != "" {
-						tmp.FontColor = styleConfig.Color
+						tmp.FontColor = strings.TrimSpace(styleConfig.Color)
+					}
+					switch styleConfig.Align {
+					case "center":
+						tmp.HorizontalType = 0
+						tmp.Ht = 0
+					case "left":
+						tmp.HorizontalType = 1
+						tmp.Ht = 1
+					case "right":
+						tmp.HorizontalType = 2
+						tmp.Ht = 2
 					}
 					tmp.Monitor = showFormatValue
 					// 如果没有showValue, 则使用value
@@ -1797,8 +1819,16 @@ func GetTableDataByMixedTableData(config [][]request.MixedTableCellDataReq, hide
 							tmp.Monitor = cell.Value
 						}
 					}
-
 				}
+				if ruleIds, ok := ruleScopeMap[i][j]; ok {
+					for _, ruleId := range ruleIds {
+						if checkCellRule(excelRuleMap[ruleId], cell.ShowValue, config) {
+							tmp.Background = strings.TrimSpace(excelRuleMap[ruleId].BackgroundColor)
+							tmp.FontColor = strings.TrimSpace(excelRuleMap[ruleId].FontColor)
+						}
+					}
+				}
+
 				dataCol = append(dataCol, tmp)
 			}
 			tableDataList = append(tableDataList, dataCol)

+ 46 - 43
services/excel/lucky_sheet_table.go

@@ -2,6 +2,7 @@ package excel
 
 import (
 	"eta_gn/eta_api/models/data_manage/excel"
+	"eta_gn/eta_api/models/data_manage/excel/request"
 	"eta_gn/eta_api/utils"
 	"fmt"
 	"sort"
@@ -106,42 +107,42 @@ func handleCellVal(tmpTableColData LuckySheetDataValue) (valueStr string) {
 }
 
 // HandleRuleToTableCell 根据管理规则渲染单元格数据
-func HandleRuleToTableCell(excelInfoId int, oldTableData TableData) (newTableData TableData, err error) {
-	newTableData = oldTableData
-	excelRuleMappingList, err := excel.GetExcelRuleMappingByExcelInfoId(excelInfoId)
-	if err != nil {
-		return
-	}
-	if len(excelRuleMappingList) == 0 {
-		return
-	}
-	tableDataList := oldTableData.TableDataList
-	excelRuleMap := make(map[int]*excel.ExcelInfoRuleMappingView)
-	for _, v := range excelRuleMappingList {
-		excelRuleMap[v.ExcelInfoRuleMappingId] = v
-	}
-	ruleScopeMap := generateRuleScopeIndexMap(excelRuleMappingList)
-	for row, scopeValues := range ruleScopeMap {
-		for col, ruleId := range scopeValues {
-			if v, ok := excelRuleMap[ruleId]; ok {
-				if len(tableDataList) > row && len(tableDataList[row]) > col {
-					// 符合管理规则要求,则进行字体和背景颜色的渲染
-					if checkCellRule(v, tableDataList[row][col].Monitor, tableDataList) {
-						tableDataList[row][col].Background = v.BackgroundColor
-						tableDataList[row][col].FontColor = v.FontColor
-					}
-				} else {
-					continue
-				}
-			} else {
-				continue
-			}
-		}
-	}
-	return
-}
+// func HandleRuleToTableCell(excelInfoId int, oldTableData TableData) (newTableData TableData, err error) {
+// 	newTableData = oldTableData
+// 	excelRuleMappingList, err := excel.GetExcelRuleMappingByExcelInfoId(excelInfoId)
+// 	if err != nil {
+// 		return
+// 	}
+// 	if len(excelRuleMappingList) == 0 {
+// 		return
+// 	}
+// 	tableDataList := oldTableData.TableDataList
+// 	excelRuleMap := make(map[int]*excel.ExcelInfoRuleMappingView)
+// 	for _, v := range excelRuleMappingList {
+// 		excelRuleMap[v.ExcelInfoRuleMappingId] = v
+// 	}
+// 	ruleScopeMap := generateRuleScopeIndexMap(excelRuleMappingList)
+// 	for row, scopeValues := range ruleScopeMap {
+// 		for col, ruleId := range scopeValues {
+// 			if v, ok := excelRuleMap[ruleId]; ok {
+// 				if len(tableDataList) > row && len(tableDataList[row]) > col {
+// 					// 符合管理规则要求,则进行字体和背景颜色的渲染
+// 					if checkCellRule(v, tableDataList[row][col].Monitor, tableDataList) {
+// 						tableDataList[row][col].Background = v.BackgroundColor
+// 						tableDataList[row][col].FontColor = v.FontColor
+// 					}
+// 				} else {
+// 					continue
+// 				}
+// 			} else {
+// 				continue
+// 			}
+// 		}
+// 	}
+// 	return
+// }
 
-func getCellValueByType(value string, valueType int, tableDataList [][]LuckySheetDataValue) (float64, bool) {
+func getCellValueByType(value string, valueType int, tableDataList [][]request.MixedTableCellDataReq) (float64, bool) {
 	if valueType == 2 {
 		coords := strings.Split(value, ",")
 		var coordIntArr []int
@@ -151,7 +152,7 @@ func getCellValueByType(value string, valueType int, tableDataList [][]LuckyShee
 		}
 		if len(coordIntArr) == 2 {
 			x, y := coordIntArr[0]-1, coordIntArr[1]-1
-			conditionValue, err := strconv.ParseFloat(tableDataList[y][x].Monitor, 64)
+			conditionValue, err := strconv.ParseFloat(tableDataList[y][x].ShowValue, 64)
 			if err != nil {
 				return 0, false
 			}
@@ -167,7 +168,7 @@ func getCellValueByType(value string, valueType int, tableDataList [][]LuckyShee
 	return 0, false
 }
 
-func checkCellRule(ruleInfo *excel.ExcelInfoRuleMappingView, value string, tableDataList [][]LuckySheetDataValue) bool {
+func checkCellRule(ruleInfo *excel.ExcelInfoRuleMappingView, value string, tableDataList [][]request.MixedTableCellDataReq) bool {
 	var tableValue float64
 	var tableTime time.Time
 	var err error
@@ -292,8 +293,8 @@ func checkCellRule(ruleInfo *excel.ExcelInfoRuleMappingView, value string, table
 	return false
 }
 
-func generateRuleScopeIndexMap(items []*excel.ExcelInfoRuleMappingView) (ruleScopeMap map[int]map[int]int) {
-	ruleScopeMap = make(map[int]map[int]int)
+func generateRuleScopeIndexMap(items []*excel.ExcelInfoRuleMappingView) (ruleScopeMap map[int]map[int][]int) {
+	ruleScopeMap = make(map[int]map[int][]int)
 	for _, item := range items {
 		coords := strings.Split(item.ScopeCoord, ",")
 		var coordIntArr []int
@@ -307,18 +308,20 @@ func generateRuleScopeIndexMap(items []*excel.ExcelInfoRuleMappingView) (ruleSco
 			for i := ymin; i <= ymax; i++ {
 				for j := xmin; j <= xmax; j++ {
 					if _, ok := ruleScopeMap[i]; !ok {
-						ruleScopeMap[i] = make(map[int]int)
+						ruleScopeMap[i] = make(map[int][]int)
 					}
-					ruleScopeMap[i][j] = item.ExcelInfoRuleMappingId
+					ruleScopeMap[i][j] = append(ruleScopeMap[i][j], item.ExcelInfoRuleMappingId)
+					// ruleScopeMap[i][j] = item.ExcelInfoRuleMappingId
 				}
 			}
 		}
 		if len(coords) == 2 {
 			x, y := coordIntArr[0]-1, coordIntArr[1]-1
 			if _, ok := ruleScopeMap[y]; !ok {
-				ruleScopeMap[y] = make(map[int]int)
+				ruleScopeMap[y] = make(map[int][]int)
 			}
-			ruleScopeMap[y][x] = item.ExcelInfoRuleMappingId
+			ruleScopeMap[y][x] = append(ruleScopeMap[y][x], item.ExcelInfoRuleMappingId)
+			// ruleScopeMap[y][x] = item.ExcelInfoRuleMappingId
 		}
 	}
 	return

+ 2 - 2
services/ppt.go

@@ -364,7 +364,7 @@ func UpdatePptEditing(pptId, status, userId int, userName string, isEn bool) (re
 			ret.Editor = userName
 			ret.Tips = fmt.Sprintf("当前%s正在编辑PPT", userName)
 			b, _ := json.Marshal(ret)
-			utils.Rc.SetNX(cacheKey, string(b), 3*time.Minute)
+			utils.Rc.SetNX(cacheKey, string(b), utils.ReportPptEditingWait*time.Second)
 			return
 		}
 
@@ -373,7 +373,7 @@ func UpdatePptEditing(pptId, status, userId int, userName string, isEn bool) (re
 			// 编辑用户与当前用户不一致, 返回编辑用户, 一致则更新缓存
 			if userId == editor.AdminId {
 				b, _ := json.Marshal(editor)
-				utils.Rc.Do("SETEX", cacheKey, int64(180), string(b))
+				utils.Rc.Do("SETEX", cacheKey, int64(utils.ReportPptEditingWait), string(b))
 			}
 			ret = editor
 			return

+ 180 - 0
services/ppt_report.go

@@ -0,0 +1,180 @@
+package services
+
+import (
+	"eta_gn/eta_api/models"
+	"eta_gn/eta_api/utils"
+	"fmt"
+	"sync"
+)
+
+// GetPptReportClassifyTreeRecursive 递归获取ppt报告分类树
+func GetPptReportClassifyTreeRecursive(list []*models.Classify, parentId int, classifyPpt map[int][]*models.PptReportItem) []*models.PptReportClassifyItem {
+	res := make([]*models.PptReportClassifyItem, 0)
+	for _, v := range list {
+		if v.ParentId == parentId {
+			t := new(models.PptReportClassifyItem)
+			t.Id = v.Id
+			t.ClassifyName = v.ClassifyName
+			t.Sort = v.Sort
+			t.ParentId = v.ParentId
+			t.Enabled = v.Enabled
+			t.Level = v.Level
+			t.HasChild = v.HasChild
+			t.ClassifyType = v.ClassifyType
+			t.ReportNum = v.ReportNum
+			t.PptList = classifyPpt[v.Id]
+			t.Child = GetPptReportClassifyTreeRecursive(list, v.Id, classifyPpt)
+			res = append(res, t)
+		}
+	}
+	return res
+}
+
+func RecursiveFilterPptNoChildTreeClassify(list []*models.PptReportClassifyItem) []*models.PptReportClassifyItem {
+	res := make([]*models.PptReportClassifyItem, 0)
+	for _, v := range list {
+		v.Child = RecursiveFilterPptNoChildTreeClassify(v.Child)
+		if len(v.Child) == 0 && v.HasChild == 1 {
+			continue
+		}
+		if len(v.Child) == 0 {
+			v.Child = nil
+		}
+		res = append(res, v)
+	}
+	return res
+}
+
+// 更新分类报告计数加个锁
+var classifyReportNumLock sync.Mutex
+
+// UpdateClassifyReportNum 更新分类报告计数
+func UpdateClassifyReportNum(classifyId int) (err error) {
+	classifyReportNumLock.Lock()
+	defer func() {
+		if err != nil {
+			utils.FileLog.Info(fmt.Sprintf("更新分类报告计数失败, %v", err))
+		}
+		classifyReportNumLock.Unlock()
+	}()
+
+	classifyItem, e := models.GetClassifyById(classifyId)
+	if e != nil {
+		err = fmt.Errorf("获取分类失败, %v", e)
+		return
+	}
+
+	// 更新分类报告数
+	var total int
+	{
+		reportOb := new(models.Report)
+		var cond string
+		switch classifyItem.Level {
+		case 1:
+			cond += ` AND classify_id_first = ?`
+		case 2:
+			cond += ` AND classify_id_second = ?`
+		case 3:
+			cond += ` AND classify_id_third = ?`
+		}
+		pars := make([]interface{}, 0)
+		pars = append(pars, classifyId)
+		count, e := reportOb.GetCountByCondition(cond, pars)
+		if e != nil {
+			err = fmt.Errorf("获取报告计数失败, %v", e)
+			return
+		}
+		total += count
+	}
+	{
+		pptOb := new(models.PptV2)
+		cond := ` AND classify_id = ?`
+		pars := make([]interface{}, 0)
+		pars = append(pars, classifyId)
+		count, e := pptOb.GetCountByCondition(cond, pars)
+		if e != nil {
+			err = fmt.Errorf("获取PPT报告计数失败, %v", e)
+			return
+		}
+		total += count
+	}
+	classifyItem.ReportNum = total
+	if e = classifyItem.UpdateClassify([]string{"ReportNum"}); e != nil {
+		err = fmt.Errorf("更新分类报告计数失败, %v", e)
+		return
+	}
+
+	// 获取所有分类, 更新父级, 无父级忽略
+	if classifyItem.ParentId <= 0 {
+		return
+	}
+	classifyOb := new(models.Classify)
+	classifies, e := classifyOb.GetItemsByCondition(``, make([]interface{}, 0), []string{}, "")
+	if e != nil {
+		err = fmt.Errorf("获取报告列表失败, %v", e)
+		return
+	}
+	classifyIdMap := make(map[int]*models.Classify)
+	classifyParentChildMap := make(map[int][]*models.Classify)
+	for _, v := range classifies {
+		classifyIdMap[v.Id] = v
+		if v.ParentId <= 0 {
+			continue
+		}
+		if classifyParentChildMap[v.ParentId] == nil {
+			classifyParentChildMap[v.ParentId] = make([]*models.Classify, 0)
+		}
+		classifyParentChildMap[v.ParentId] = append(classifyParentChildMap[v.ParentId], v)
+	}
+
+	// 递归获取需要更新的父级报告数
+	updateClassifies := CountParentClassifyReportNumRecursive(classifies, classifyItem.ParentId, classifyIdMap, classifyParentChildMap)
+	if len(updateClassifies) == 0 {
+		return
+	}
+	for _, v := range updateClassifies {
+		if e = v.UpdateClassify([]string{"ReportNum"}); e != nil {
+			err = fmt.Errorf("更新父级分类报告计数失败, %v", e)
+			return
+		}
+	}
+	return
+}
+
+// CountParentClassifyReportNumRecursive 递归统计父级分类报告数
+func CountParentClassifyReportNumRecursive(list []*models.Classify, parentId int, classifyIdMap map[int]*models.Classify, classifyParentChildMap map[int][]*models.Classify) []*models.Classify {
+	res := make([]*models.Classify, 0)
+	for _, v := range list {
+		// 找父级
+		if v.Id != parentId {
+			continue
+		}
+		parentItem := classifyIdMap[v.Id]
+		if parentItem == nil {
+			break
+		}
+
+		// 合计所有直系子分类报告数
+		children := classifyParentChildMap[v.Id]
+		if len(children) == 0 {
+			break
+		}
+		var t int
+		for _, child := range children {
+			t += child.ReportNum
+		}
+		parentItem.ReportNum = t
+		res = append(res, parentItem)
+
+		// 继续向上统计父级
+		if v.ParentId <= 0 {
+			break
+		}
+		parents := CountParentClassifyReportNumRecursive(list, v.ParentId, classifyIdMap, classifyParentChildMap)
+		if len(parents) == 0 {
+			break
+		}
+		res = append(res, parents...)
+	}
+	return res
+}

+ 2 - 2
services/report.go

@@ -818,9 +818,9 @@ func UpdateReportEditMark(reportId, reportChapterId, nowUserId, status int, nowU
 			return
 		}
 		if opUserId > 0 {
-			utils.Rc.Do("SETEX", key, int64(60), string(bt)) //3分钟缓存
+			utils.Rc.Do("SETEX", key, int64(utils.ReportPptEditingWait), string(bt)) //3分钟缓存
 		} else {
-			utils.Rc.SetNX(key, string(bt), time.Second*60*1) //3分钟缓存
+			utils.Rc.SetNX(key, string(bt), utils.ReportPptEditingWait*time.Second) //3分钟缓存
 		}
 	} else if status == 3 {
 		//完成编辑,开始清除编辑缓存

+ 115 - 0
services/report_outer.go

@@ -0,0 +1,115 @@
+package services
+
+import (
+	"encoding/json"
+	"eta_gn/eta_api/models"
+	"eta_gn/eta_api/utils"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+	"strings"
+)
+
+// OuterReportCallBackApiUrl 外部报告回调接口
+const OuterReportCallBackApiUrl = "/subject/report/writingCallback"
+
+// OuterReportCallBackRequest 外部报告回调请求
+type OuterReportCallBackRequest struct {
+	Name     string `json:"name"`
+	ReportId int    `json:"reportId"`
+	Url      string `json:"url"`
+	FileType string `json:"fileType"`
+	FileSize int    `json:"fileSize"`
+}
+
+// OuterReportCallBackResp 外部报告回调响应
+type OuterReportCallBackResp struct {
+	Code int    `json:"code"`
+	Msg  string `json:"msg"`
+	Data bool   `json:"data"`
+}
+
+// OuterReportCallBack 外部报告回调(提交审批)
+func OuterReportCallBack(outReportId int, title, fileUrl, fileType string) (err error) {
+	var requestUrl, params, respBody string
+	defer func() {
+		if err != nil {
+			tips := fmt.Sprintf("OuterReportCallBack-外部报告回调失败, Url: %s, Request: %s, Resp: %s, ErrMsg: %v", requestUrl, params, respBody, err)
+			fmt.Println(tips)
+			utils.FileLog.Info(tips)
+			return
+		}
+		utils.FileLog.Info(fmt.Sprintf("OuterReportCallBack-回调成功, Url: %s, Request: %s, Resp: %s", requestUrl, params, respBody))
+	}()
+
+	// 入参
+	var req OuterReportCallBackRequest
+	req.Name = fmt.Sprintf("%s%s", title, fileType)
+	req.ReportId = outReportId
+	req.Url = fileUrl
+	req.FileType = fileType
+	b, e := json.Marshal(req)
+	if e != nil {
+		err = fmt.Errorf("请求参数JSON格式化失败, %v", e)
+		return
+	}
+	params = string(b)
+
+	// 获取地址
+	conf, e := models.GetBusinessConfByKey(models.BusinessConfOuterReportApiUrl)
+	if e != nil {
+		if utils.IsErrNoRow(e) {
+			err = fmt.Errorf("外部报告API地址未配置")
+		}
+		err = fmt.Errorf("获取外部报告API地址配置失败, %v", e)
+		return
+	}
+	if conf.ConfVal == "" {
+		err = fmt.Errorf("外部报告API地址为空")
+		return
+	}
+	requestUrl = conf.ConfVal + OuterReportCallBackApiUrl
+
+	// 请求接口
+	resByte, e := OuterReportCallBackPut(requestUrl, params, "application/json;charset=utf-8")
+	if e != nil {
+		err = fmt.Errorf("接口请求失败, %v", e)
+		return
+	}
+	respBody = string(resByte)
+	var resp OuterReportCallBackResp
+	if e := json.Unmarshal(resByte, &resp); e != nil {
+		err = fmt.Errorf("响应JSON解析失败, %v", e)
+		return
+	}
+	if resp.Code != 200 {
+		err = fmt.Errorf("回调失败, Code: %d, Msg: %s", resp.Code, resp.Msg)
+		return
+	}
+	return
+}
+
+// OuterReportCallBackPut 外部报告回调POST
+func OuterReportCallBackPut(url, postData string, params ...string) ([]byte, error) {
+	body := ioutil.NopCloser(strings.NewReader(postData))
+	client := &http.Client{}
+	req, e := http.NewRequest("PUT", url, body)
+	if e != nil {
+		return nil, fmt.Errorf("http request err: %v", e)
+	}
+	contentType := "application/x-www-form-urlencoded;charset=utf-8"
+	if len(params) > 0 && params[0] != "" {
+		contentType = params[0]
+	}
+	req.Header.Set("Content-Type", contentType)
+	resp, e := client.Do(req)
+	if e != nil {
+		return nil, fmt.Errorf("client do err: %v", e)
+	}
+	defer resp.Body.Close()
+	b, e := ioutil.ReadAll(resp.Body)
+	if e != nil {
+		return nil, fmt.Errorf("read body err: %v", e)
+	}
+	return b, e
+}

+ 173 - 5
services/report_v2.go

@@ -103,8 +103,12 @@ func AddReportAndChapter(reportInfo *models.Report, inheritReportId int, grantAd
 	}
 
 	// 报告权限处理
-	go handleReportPermission(reportId, minClassifyId)
+	go func() {
+		handleReportPermission(reportId, minClassifyId)
 
+		// 更新报告计数
+		_ = UpdateClassifyReportNum(minClassifyId)
+	}()
 	return
 }
 
@@ -172,9 +176,10 @@ func EditReport(reportInfo *models.Report, req models.EditReq, sysUser *system.A
 	}
 	//reportInfo.CollaborateType = req.CollaborateType
 	//reportInfo.ReportLayout = req.ReportLayout
-	if req.IsPublicPublish <= 0 {
-		req.IsPublicPublish = 1
-	}
+	//if req.IsPublicPublish <= 0 {
+	//	req.IsPublicPublish = 1
+	//}
+	req.IsPublicPublish = 2 // 本地报告固定不公开
 	reportInfo.IsPublicPublish = req.IsPublicPublish
 	reportInfo.LastModifyAdminId = sysUser.AdminId
 	reportInfo.LastModifyAdminName = sysUser.RealName
@@ -1275,6 +1280,19 @@ func DeleteReportAndChapter(reportId int) (err error) {
 	// 重置PPT关联报告
 	go func() {
 		_ = ResetPPTReport(reportId, false)
+
+		// 更新报告计数
+		classifyId := reportInfo.ClassifyIdThird
+		if classifyId <= 0 {
+			classifyId = reportInfo.ClassifyIdSecond
+		}
+		if classifyId <= 0 {
+			classifyId = reportInfo.ClassifyIdFirst
+		}
+		if classifyId <= 0 {
+			return
+		}
+		_ = UpdateClassifyReportNum(classifyId)
 	}()
 
 	return
@@ -1346,7 +1364,7 @@ func handleReportPermission(reportId int64, minClassifyId int) {
 	}
 
 	// 同步crm权限
-	_ = EditReportPermissionSync(reportId, minClassifyId)
+	//_ = EditReportPermissionSync(reportId, minClassifyId)
 
 	return
 }
@@ -1457,3 +1475,153 @@ func GetReportWaterMarkPdf(reportInfo *models.Report, sysUser *system.Admin) {
 	waterMarkStr := fmt.Sprintf("%s - %s", sysUser.RealName, sysUser.Mobile)
 	GeneralWaterMarkPdf(filePath, waterMarkStr)
 }
+
+// PublishReportV2
+// @Description: 报告发布
+// @author: Roc
+// @datetime 2024-06-20 09:44:13
+// @param reportId int
+// @param reportUrl string
+// @param sysUser *system.Admin
+// @return tips string
+// @return err error
+// @return errMsg string
+func PublishReportV2(reportId int, sysUser *system.Admin) (tips string, err error, errMsg string) {
+	errMsg = `操作失败`
+
+	// 获取报告
+	reportInfo, err := models.GetReportByReportId(reportId)
+	if err != nil {
+		if utils.IsErrNoRow(err) {
+			errMsg = "报告不存在, 请刷新页面"
+			return
+		}
+		return
+	}
+	if reportInfo == nil {
+		err = fmt.Errorf("报告信息有误, ReportId: %d", reportId)
+		return
+	}
+	if reportInfo.ReportSource == utils.ReportSourceLocal && reportInfo.State != models.ReportStateUnpublished {
+		errMsg = "报告状态异常"
+		err = fmt.Errorf("报告状态异常, ReportId: %d, State: %d", reportId, reportInfo.State)
+		return
+	}
+	if reportInfo.ReportSource == utils.ReportSourceOuter && reportInfo.State != models.ReportStateWaitSubmit && reportInfo.State != models.ReportStateRefused {
+		errMsg = "报告状态异常"
+		err = fmt.Errorf("外部报告状态异常, ReportId: %d, State: %d", reportId, reportInfo.State)
+		return
+	}
+	stateOrigin := reportInfo.State
+
+	// 如果有章节,那么校验章节的状态
+	chapters := make([]*models.ReportChapter, 0)
+	if reportInfo.HasChapter == 1 {
+		cps, e := models.GetChapterListByReportId(reportId)
+		if e != nil {
+			err = fmt.Errorf("获取报告章节失败, ReportId: %d, Err: %v", reportId, e)
+			return
+		}
+		chapters = cps
+		if len(chapters) <= 0 {
+			tips = "报告章节为空,不可发布"
+			errMsg = tips
+			err = errors.New(tips)
+			return
+		}
+		for _, chapter := range chapters {
+			if chapter.PublishState == 1 {
+				tips = "还存在未发布的章节"
+				errMsg = tips
+				err = errors.New(tips)
+				return
+			}
+		}
+	}
+
+	// 非章节报告
+	if reportInfo.HasChapter == 0 && reportInfo.Content == "" {
+		errMsg = `报告内容为空,不可发布`
+		err = errors.New("报告内容为空,不需要生成,report_id:" + strconv.Itoa(reportId))
+		return
+	}
+
+	// 报告变更后的状态
+	stateMap := map[int]int{
+		models.ReportStateUnpublished: models.ReportStatePublished,   // 未发布->已发布
+		models.ReportStateWaitSubmit:  models.ReportStateWaitApprove, // 待提交->待审批
+		models.ReportStateRefused:     models.ReportStateWaitApprove, // 已驳回->待审批
+	}
+	state := stateMap[reportInfo.State]
+	if state == 0 {
+		err = fmt.Errorf("报告当前状态异常, ReportId: %d, State: %d")
+		return
+	}
+	// 如果报告曾经发布过,并且已经发送过模版消息,则章节的发布时间为报告的发布时间
+	var publishTime time.Time
+	if reportInfo.MsgIsSend == 1 && reportInfo.PublishTime.IsZero() {
+		publishTime = reportInfo.PublishTime
+	} else {
+		publishTime = time.Now()
+	}
+	reportInfo.State = state
+	reportInfo.PublishTime = publishTime
+	reportInfo.PrePublishTime = nil // 重置预发布时间
+	reportInfo.PreMsgSend = 0       // 重置预发送消息状态
+	reportInfo.ModifyTime = time.Now()
+	reportInfo.LastModifyAdminId = sysUser.AdminId
+	reportInfo.LastModifyAdminName = sysUser.RealName
+	updateCols := []string{"State", "PublishTime", "PrePublishTime", "PreMsgSend", "ModifyTime", "LastModifyAdminId", "LastModifyAdminName"}
+
+	// 发布报告和章节
+	if e := models.PublishReportAndChapter(reportInfo, true, updateCols); e != nil {
+		err = errors.New("发布报告及章节失败")
+		return
+	}
+
+	// 后置处理
+	go func() {
+		// 发布时备份内容
+		SaveReportLogs(reportInfo, chapters, reportInfo.AdminId, reportInfo.AdminRealName)
+
+		// 更新ES
+		_ = UpdateReportEs(reportId, 2)
+
+		// 记录日志
+		recordItem := &models.ReportStateRecord{
+			ReportId:   reportId,
+			ReportType: 1,
+			State:      state,
+			AdminId:    sysUser.AdminId,
+			AdminName:  sysUser.AdminName,
+			CreateTime: time.Now(),
+		}
+		_, _ = models.AddReportStateRecord(recordItem)
+
+		// 报告权限
+		//minClassifyId, _, e := getMinClassify(reportInfo)
+		//if e != nil {
+		//	utils.FileLog.Info(fmt.Sprintf("报告权限处理失败, ReportId: %d, Err: %v", reportId, e))
+		//	return
+		//}
+		//handleReportPermission(int64(reportInfo.Id), minClassifyId)
+	}()
+
+	// 生成报告pdf和长图, 外部报告回调发起审批
+	go func() {
+		reportPdfUrl := GetGeneralPdfUrl(reportInfo.ReportCode, reportInfo.ClassifyNameFirst, reportInfo.ReportLayout)
+		_, pdfUrl := Report2pdfAndJpeg(reportPdfUrl, reportId, 1)
+		if reportInfo.ReportSource != utils.ReportSourceOuter {
+			return
+		}
+
+		// 回调智力共享审批, 若请求失败则恢复提交前状态(先这么处理吧,允许再次提交)
+		outId, _ := strconv.Atoi(reportInfo.OutReportId)
+		e := OuterReportCallBack(outId, reportInfo.Title, pdfUrl, ".pdf")
+		if e != nil {
+			reportInfo.State = stateOrigin
+			_ = reportInfo.Update([]string{"State"})
+		}
+	}()
+	return
+}

+ 3 - 1
services/smart_report.go

@@ -165,7 +165,7 @@ finally:
 // @param reportUrl string
 // @param reportId int
 // @param reportType int
-func Report2pdfAndJpeg(reportUrl string, reportId, reportType int) {
+func Report2pdfAndJpeg(reportUrl string, reportId, reportType int) (imgUrl, pdfUrl string) {
 	var err error
 
 	defer func() {
@@ -230,6 +230,7 @@ func Report2pdfAndJpeg(reportUrl string, reportId, reportType int) {
 	defer func() {
 		_ = os.Remove(pdfPath)
 	}()
+	pdfUrl = resourceUrl
 
 	// 更新pdf url
 	err = models.ModifyReportPdfUrl(reportId, resourceUrl)
@@ -271,6 +272,7 @@ func Report2pdfAndJpeg(reportUrl string, reportId, reportType int) {
 	defer func() {
 		_ = os.Remove(jpegPath)
 	}()
+	imgUrl = resourceUrl
 
 	err = models.ModifyReportImgUrl(reportId, resourceUrl)
 	if err != nil {

+ 66 - 0
services/task.go

@@ -1,9 +1,11 @@
 package services
 
 import (
+	"eta_gn/eta_api/models"
 	"eta_gn/eta_api/services/data"
 	"eta_gn/eta_api/utils"
 	"fmt"
+	"strconv"
 	"strings"
 )
 
@@ -19,6 +21,9 @@ func Task() {
 	// 进行指标替换操作
 	go DealReplaceEdbCache()
 
+	// 修复分类LevelPath
+	//FixClassifyLevelPath()
+
 	fmt.Println("task end")
 }
 
@@ -39,3 +44,64 @@ func ImportManualDataRefresh() {
 		})
 	}
 }
+
+func FixClassifyLevelPath() {
+	var err error
+	defer func() {
+		if err != nil {
+			fmt.Println(err)
+		}
+	}()
+	classifyOb := new(models.Classify)
+	classifies, e := classifyOb.GetItemsByCondition("", []interface{}{}, []string{}, "")
+	if e != nil {
+		err = fmt.Errorf("获取分类列表失败, %v", e)
+		return
+	}
+	fmt.Println("开始修复")
+
+	// 先更新所有一级分类
+	for _, v := range classifies {
+		if v.ParentId > 0 {
+			continue
+		}
+		v.LevelPath = strconv.Itoa(v.Id)
+		if e = v.UpdateClassify([]string{"LevelPath"}); e != nil {
+			err = fmt.Errorf("更新LevelPath失败, ID: %d", v.Id)
+			return
+		}
+	}
+
+	// 再更新二级
+	parentMap := make(map[int]string)
+	for _, v := range classifies {
+		if v.Level != 2 {
+			continue
+		}
+		v.LevelPath = fmt.Sprintf("%d,%d", v.ParentId, v.Id)
+		if e = v.UpdateClassify([]string{"LevelPath"}); e != nil {
+			err = fmt.Errorf("更新二级LevelPath失败, ID: %d", v.Id)
+			return
+		}
+		parentMap[v.Id] = v.LevelPath
+	}
+
+	// 再更新三级,没四级了
+	for _, v := range classifies {
+		if v.Level != 3 {
+			continue
+		}
+		str := parentMap[v.ParentId]
+		if str == "" {
+			err = fmt.Errorf("二级LevelPath为空, ID: %d", v.ParentId)
+			return
+		}
+
+		v.LevelPath = fmt.Sprintf("%s,%d", str, v.Id)
+		if e = v.UpdateClassify([]string{"LevelPath"}); e != nil {
+			err = fmt.Errorf("更新三级LevelPath失败, ID: %d", v.Id)
+			return
+		}
+	}
+	fmt.Println("修复成功")
+}

+ 14 - 5
utils/common.go

@@ -40,13 +40,12 @@ import (
 	xhtml "golang.org/x/net/html"
 )
 
-// 随机数种子
-var rnd = rand.New(rand.NewSource(time.Now().UnixNano()))
-
 func GetRandString(size int) string {
 	allLetterDigit := []string{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "!", "@", "#", "$", "%", "^", "&", "*"}
 	randomSb := ""
 	digitSize := len(allLetterDigit)
+	// 随机数种子
+	rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
 	for i := 0; i < size; i++ {
 		randomSb += allLetterDigit[rnd.Intn(digitSize)]
 	}
@@ -57,6 +56,8 @@ func GetRandStringNoSpecialChar(size int) string {
 	allLetterDigit := []string{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"}
 	randomSb := ""
 	digitSize := len(allLetterDigit)
+	// 随机数种子
+	rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
 	for i := 0; i < size; i++ {
 		randomSb += allLetterDigit[rnd.Intn(digitSize)]
 	}
@@ -98,11 +99,15 @@ func HmacMd5(key, data string) string {
 
 // 获取数字随机字符
 func GetRandDigit(n int) string {
+	// 随机数种子
+	rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
 	return fmt.Sprintf("%0"+strconv.Itoa(n)+"d", rnd.Intn(int(math.Pow10(n))))
 }
 
 // 获取随机数
 func GetRandNumber(n int) int {
+	// 随机数种子
+	rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
 	return rnd.Intn(n)
 }
 
@@ -593,7 +598,7 @@ func Sha1(data string) string {
 }
 
 func GetVideoPlaySeconds(videoPath string) (playSeconds float64, err error) {
-	cmd := `ffmpeg -i ` + videoPath + `  2>&1 | grep 'Duration' | cut -d ' ' -f 4 | sed s/,//`
+	cmd := fmt.Sprintf(`ffmpeg -i %s  2>&1 | grep 'Duration' | cut -d ' ' -f 4 | sed s/,//`, videoPath)
 	out, err := exec.Command("bash", "-c", cmd).Output()
 	if err != nil {
 		return
@@ -1760,7 +1765,11 @@ func GetDateByDateTypeV2(dateType int, tmpStartDate, tmpEndDate string, startYea
 			startDate = startDate + "-01"
 		}
 		if strings.Count(endDate, "-") == 1 {
-			endDate = endDate + "-01"
+			endTime, err := time.Parse(FormatYearMonthDate, endDate)
+			if err != nil {
+				return
+			}
+			endDate = endTime.AddDate(0, 1, -1).Format(FormatDate)
 		}
 	}
 

+ 13 - 22
utils/constants.go

@@ -37,12 +37,6 @@ const (
 	RegularEmail  = `\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*`                                             //匹配电子邮箱
 )
 
-// 验证码code
-const (
-	REGISTER_CODE = iota + 1 //注册
-	LOGIN_CODE               //登录
-)
-
 // 管理员,ficc管理员,ficc销售,权益管理员,权益销售。
 // 角色类型/类型编码
 const (
@@ -87,9 +81,6 @@ const (
 	COMPANY_PRODUCT_RAI_NAME  = "权益"
 )
 
-var PermissionFiccClassifyArr = [...]string{"宏观经济", "化工产业", "建材产业", "有色产业", "新能源", "市场策略"}
-var PermissionAllClassifyArr = [...]string{"宏观经济", "化工产业", "建材产业", "有色产业", "新能源", "市场策略", "权益"}
-
 //apply_method:申请类型:1:试用->正式,2:冻结—>试用,3:流失—>正式,4:试用延期,5:原销售申请领取流失客户,6:正式客户申请服务更新
 
 // 数据来源渠道
@@ -197,8 +188,6 @@ const (
 	EDB_DATA_LIMIT = 10
 )
 
-var Hz_Data_WIND_Url_List = []string{"http://datawind.hzinsights.com:8040/", "http://datawind2.hzinsights.com:8040/"}
-
 const (
 	HZ_CHART_LIB_DETAIL             = "HZ_CHART_LIB_DETAIL_"            //图表数据缓存
 	HZ_CHART_LIB_EXCEL_TABLE_DETAIL = "HZ_CHART_LIB_EXCEL_TABLE_DETAIL" //excel表格数据缓存
@@ -325,11 +314,6 @@ const (
 	AiChatLimit = 500
 )
 
-// 系统来源
-const (
-	SOURCE_ETA_FLAG = 2
-)
-
 // BusinessCodeSalt 商家编码盐值
 const BusinessCodeSalt = "dr7WY0OZgGR7upw1"
 
@@ -343,12 +327,6 @@ const CrmEtaAuthorization = "NIi1RbEmH0C2rksXtPGDPBBgRgTZY87Q"
 
 const LoginCacheTime = 8 * 60 // 登录缓存时长, 分钟
 
-// 对象存储客户端
-const (
-	STORAGESOURCE_OSS   = 1 //阿里云OSS
-	STORAGESOURCE_MINIO = 2 //MinIo
-)
-
 const (
 	STORAGESOURCE_OSS_NAME   = "oss"
 	STORAGESOURCE_MINIO_NAME = "minio"
@@ -479,3 +457,16 @@ const (
 
 // BaseEdbRefreshStartDate 指标的基础刷新开始日期
 const BaseEdbRefreshStartDate = `1899-01-01`
+
+const (
+	ReportTypeDefault     = 1 // 报告类型-默认研报
+	ReportTypePPT         = 2 // 报告类型-PPT
+	ReportSourceLocal     = 1 // 报告来源-本地
+	ReportSourceOuter     = 2 // 报告来源-外部
+	ReportAddTypeNew      = 1 // 报告新增方式-新增
+	ReportAddTypeInherit  = 2 // 报告新增方式-继承
+	ReportWriteTypeSingle = 1 // 报告协作方式-个人
+	ReportWriteTypeGroup  = 2 // 报告协作方式-多人
+)
+
+const ReportPptEditingWait = 30 // 报告/PPT编辑退出后其他人的等待时长(单位:s)