Browse Source

Merge branch 'master' into eta_2.4.2_area_graph_1216@guomengyuan

kobe6258 1 month ago
parent
commit
0b2db448ba
90 changed files with 22182 additions and 49 deletions
  1. 7 2
      controllers/admin.go
  2. 89 12
      controllers/chart_classify.go
  3. 324 0
      controllers/chart_collect_stat.go
  4. 5 5
      controllers/chart_info.go
  5. 68 0
      controllers/company_industry.go
  6. 2190 0
      controllers/company_seller.go
  7. 135 0
      controllers/crm_config.go
  8. 1861 0
      controllers/eta_business/eta_business.go
  9. 764 0
      controllers/eta_business/eta_business_menu.go
  10. 1369 0
      controllers/eta_business/user.go
  11. 726 0
      controllers/eta_training_video/eta_training_video.go
  12. 404 0
      controllers/eta_training_video/eta_training_video_classify.go
  13. 346 0
      controllers/eta_training_video/eta_training_video_tag.go
  14. 1929 0
      controllers/eta_trial.go
  15. 401 0
      controllers/eta_version_update_log.go
  16. 539 0
      controllers/help_doc/classify.go
  17. 448 0
      controllers/help_doc/doc.go
  18. 167 0
      controllers/resource.go
  19. 72 0
      controllers/sys_menu.go
  20. 334 0
      controllers/sys_role.go
  21. 28 7
      go.mod
  22. 96 0
      go.sum
  23. 1 1
      main.go
  24. 41 0
      models/chart_classify.go
  25. 169 0
      models/chart_collect/chart.go
  26. 61 0
      models/chart_collect/log.go
  27. 106 0
      models/chart_collect/stat.go
  28. 17 0
      models/chart_info.go
  29. 180 0
      models/company/company_approval_message.go
  30. 48 0
      models/company/company_industry.go
  31. 25 0
      models/company/company_seller.go
  32. 77 0
      models/db.go
  33. 57 0
      models/eta_business/business_chart_classify_permission.go
  34. 374 0
      models/eta_business/eta_business.go
  35. 118 0
      models/eta_business/eta_business_config_relate.go
  36. 205 0
      models/eta_business/eta_business_contract.go
  37. 227 0
      models/eta_business/eta_business_menu.go
  38. 117 0
      models/eta_business/eta_business_menu_icon.go
  39. 133 0
      models/eta_business/eta_business_menu_relate.go
  40. 138 0
      models/eta_business/eta_business_operation_record.go
  41. 303 0
      models/eta_training_video/eta_training_video.go
  42. 162 0
      models/eta_training_video/eta_training_video_classify.go
  43. 137 0
      models/eta_training_video/eta_training_video_classify_relate.go
  44. 136 0
      models/eta_training_video/eta_training_video_op_logs.go
  45. 179 0
      models/eta_training_video/eta_training_video_tag.go
  46. 142 0
      models/eta_training_video/eta_training_video_tag_relate.go
  47. 122 0
      models/eta_training_video/eta_training_video_view_log.go
  48. 177 0
      models/eta_trial/eta_trial.go
  49. 226 0
      models/eta_trial/eta_trial_approval.go
  50. 71 0
      models/eta_trial/eta_trial_manual.go
  51. 155 0
      models/eta_trial/eta_trial_questionnaire.go
  52. 135 0
      models/eta_trial/eta_trial_questionnaire_record.go
  53. 33 0
      models/eta_trial/eta_trial_record.go
  54. 79 0
      models/eta_trial/sys_user.go
  55. 144 0
      models/eta_version_update_log.go
  56. 260 0
      models/help_doc/classify.go
  57. 198 0
      models/help_doc/help_doc.go
  58. 182 0
      models/system/admin.go
  59. 132 0
      models/system/crm_config.go
  60. 2 9
      models/system/eta_business.go
  61. 147 0
      models/system/sys_department.go
  62. 254 0
      models/system/sys_group.go
  63. 214 0
      models/system/sys_menu.go
  64. 87 0
      models/system/sys_menu_button.go
  65. 269 0
      models/system/sys_role.go
  66. 256 0
      models/system/sys_role_admin.go
  67. 84 0
      models/system/sys_team.go
  68. 218 0
      models/user.go
  69. 891 0
      routers/commentsRouter.go
  70. 61 0
      routers/router.go
  71. 162 0
      services/aws_s3.go
  72. 55 2
      services/chart_classify.go
  73. 150 0
      services/chart_collect_elastic.go
  74. 122 0
      services/eta_business/eta_business.go
  75. 46 0
      services/eta_business/eta_business_menu.go
  76. 16 0
      services/eta_business/user.go
  77. 214 0
      services/eta_forum_hub/eta_trial.go
  78. 108 0
      services/eta_training_video/eta_training_video.go
  79. 46 0
      services/eta_training_video/eta_training_video_classify.go
  80. 254 0
      services/eta_trial.go
  81. 32 0
      services/help_doc_classify.go
  82. 461 0
      services/minio.go
  83. 297 0
      services/oss.go
  84. 110 0
      services/qichacha.go
  85. 2 8
      services/system.go
  86. BIN
      static/template/用户导入模版.xlsx
  87. 64 0
      utils/common.go
  88. 148 2
      utils/config.go
  89. 27 1
      utils/constants.go
  90. 16 0
      utils/des3.go

+ 7 - 2
controllers/admin.go

@@ -30,13 +30,13 @@ func (this *AdminController) GetBusinessAdmin() {
 		return
 	}
 	// 查询所有机构
-	ob := new(system.EtaBusiness)
+	/*ob := new(system.EtaBusiness)
 	companyList, err := ob.GetItemsByCondition("", make([]interface{}, 0), []string{}, "")
 	if err != nil {
 		br.Msg = "获取机构信息失败"
 		br.ErrMsg = err.Error()
 		return
-	}
+	}*/
 	list := make([]*system.BusinessAdmin, 0)
 	// todo 查询上传图表的所有用户ID
 	creators, err := models.GetChartCreators()
@@ -75,6 +75,11 @@ func (this *AdminController) GetBusinessAdmin() {
 			companyMap[v.BusinessCode] = append(companyMap[v.BusinessCode], tmp)
 		}
 	}
+	companyList := make([]*system.HzEtaBusiness, 0)
+	hzCompany := new(system.HzEtaBusiness)
+	hzCompany.BusinessName = utils.HzCompanyName
+	hzCompany.BusinessCode = utils.HzBusinessCodeRelease
+	companyList = append(companyList, hzCompany)
 	if len(companyList) > 0 {
 		for _, v := range companyList {
 			tmp := &system.BusinessAdmin{

+ 89 - 12
controllers/chart_classify.go

@@ -217,7 +217,7 @@ func (this *ChartClassifyController) SimpleList() {
 
 		if len(allChartInfo) > 0 {
 			// 查询机构名称
-			adminIds := make([]int, 0)
+			/*adminIds := make([]int, 0)
 			for _, v := range allChartInfo {
 				adminIds = append(adminIds, v.SysUserId)
 			}
@@ -227,12 +227,12 @@ func (this *ChartClassifyController) SimpleList() {
 				br.Msg = "获取失败"
 				br.ErrMsg = "获取机构失败,Err:" + er.Error()
 				return
-			}
+			}*/
 			for _, v := range allChartInfo {
 				v.Children = make([]*models.ChartClassifyItems, 0)
-				if n, ok := adminCompanyMap[v.SysUserId]; ok {
-					v.Company = n
-				}
+				//if n, ok := adminCompanyMap[v.SysUserId]; ok {
+				v.Company = utils.HzCompanyName
+				//}
 				nodeAll = append(nodeAll, v)
 			}
 		}
@@ -891,7 +891,7 @@ func (this *ChartClassifyController) ChartClassifyChartListV2() {
 	}
 
 	if len(allChartInfo) > 0 {
-		adminIds := make([]int, 0)
+		/*adminIds := make([]int, 0)
 		for _, v := range allChartInfo {
 			adminIds = append(adminIds, v.SysUserId)
 		}
@@ -901,12 +901,12 @@ func (this *ChartClassifyController) ChartClassifyChartListV2() {
 			br.Msg = "获取失败"
 			br.ErrMsg = "获取机构失败,Err:" + er.Error()
 			return
-		}
+		}*/
 		for k, v := range allChartInfo {
 			v.Children = make([]*models.ChartClassifyItems, 0)
-			if n, ok := adminCompanyMap[v.SysUserId]; ok {
-				allChartInfo[k].Company = n
-			}
+			//if n, ok := adminCompanyMap[v.SysUserId]; ok {
+			allChartInfo[k].Company = utils.HzCompanyName
+			//}
 		}
 	}
 	page := paging.GetPaging(currentIndex, pageSize, int(total))
@@ -963,7 +963,7 @@ func (this *ChartClassifyController) ModifyChartClassify() {
 		chartClassifyIds := req.ChartClassifyIds
 		sysUserIds := req.SysUserIds
 		chartName := req.ChartName
-
+		chartInfoIds := req.ChartInfoIds
 		condition := " AND source = ? "
 		pars := make([]interface{}, 0)
 		pars = append(pars, utils.CHART_SOURCE_DEFAULT)
@@ -987,6 +987,24 @@ func (this *ChartClassifyController) ModifyChartClassify() {
 			pars = append(pars, classifyIdSlice)
 		}
 
+		if chartInfoIds != "" {
+			chartIdStr := strings.Split(chartInfoIds, ",")
+			if len(chartIdStr) == 0 {
+				br.Msg = "请选择正确的图表"
+			}
+			notChartIds := make([]int, 0)
+			for _, id := range chartIdStr {
+				tmp, e := strconv.Atoi(id)
+				if e != nil {
+					br.Msg = "请选择正确的图表"
+					return
+				}
+				notChartIds = append(notChartIds, tmp)
+			}
+			condition += "  AND chart_info_id not in (" + utils.GetOrmInReplace(len(notChartIds)) + ") "
+			pars = append(pars, notChartIds)
+		}
+
 		if sysUserIds != "" {
 			adminIds := strings.Split(sysUserIds, ",")
 			if len(adminIds) == 0 {
@@ -1006,7 +1024,7 @@ func (this *ChartClassifyController) ModifyChartClassify() {
 			pars = append(pars, adminIdsSlice)
 		}
 		if chartName != "" {
-			condition = " AND chart_name LIKE ? "
+			condition += " AND chart_name LIKE ? "
 			pars = append(pars, utils.GetLikeKeyword(chartName))
 		}
 		allChartInfo, err := models.GetChartInfoItemsByCondition(condition, pars)
@@ -1056,6 +1074,12 @@ func (this *ChartClassifyController) ModifyChartClassify() {
 		return
 	}
 
+	go func() {
+		for _, id := range chartIds {
+			services.EsAddOrEditChartInfo(id)
+			services.EsAddOrEditChartCollectByChartInfoId(id)
+		}
+	}()
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "操作成功"
@@ -1123,3 +1147,56 @@ func (this *ChartClassifyController) ClassifyTree() {
 	br.Msg = "获取成功"
 	br.Data = resp
 }
+
+// ClassifyRealTree
+// @Title 多层分类列表树
+// @Description 多层分类列表树
+// @Success 200 {object} data_manage.EdbClassifyListResp
+// @router /classify/real_tree [get]
+func (this *ChartClassifyController) ClassifyRealTree() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	allList, err := models.GetChartClassify()
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+	nodeAll := make([]*models.ChartClassifyItems, 0)
+	list := make([]*models.ChartClassifyItems, 0)
+	var sortList models.ChartClassifyItemList
+
+	if len(allList) > 0 {
+		nodeAll = services.GetClassifyTreeRecursive(allList, 0)
+		//根据sort值排序
+		sortList = nodeAll
+		sort.Sort(sortList)
+	}
+
+	language := `CN`
+	// 指标显示的语言
+	{
+		configDetail, _ := system.GetConfigDetailByCode(this.SysUser.AdminId, system.EdbLanguageVar)
+		if configDetail != nil {
+			language = configDetail.ConfigValue
+		} else {
+			configDetail, _ = system.GetDefaultConfigDetailByCode(system.EdbLanguageVar)
+			if configDetail != nil {
+				language = configDetail.ConfigValue
+			}
+		}
+	}
+
+	list = append(list, sortList...)
+	resp := new(models.ChartClassifyListResp)
+	resp.AllNodes = list
+	resp.Language = language
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}

+ 324 - 0
controllers/chart_collect_stat.go

@@ -0,0 +1,324 @@
+package controllers
+
+import (
+	"eta/eta_forum_admin/models"
+	"eta/eta_forum_admin/models/chart_collect"
+	"eta/eta_forum_admin/models/eta_business"
+	"eta/eta_forum_admin/utils"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"strconv"
+	"strings"
+	"time"
+)
+
+type ChartCollectStatController struct {
+	BaseAuthController
+}
+
+// UserStat
+// @Title 用户纬度统计收藏数据
+// @Description 用户纬度统计收藏数据
+// @Success 200 {object} data_manage.EdbClassifyListResp
+// @router /stat/user [get]
+func (this *ChartCollectStatController) UserStat() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	// 默认查一级分类和一级分类下的指标信息,
+	// 如果是 子级分类,查询该子级分类的下一级分类和指标信息
+	// 增加标识判断是文件夹还是指标列表
+	startDate := this.GetString("StartDate")
+	endDate := this.GetString("EndDate")
+	etaBusinessIds := this.GetString("EtaBusinessIds")
+	pageSize, _ := this.GetInt("PageSize")
+	currentIndex, _ := this.GetInt("CurrentIndex")
+
+	sortType := this.GetString("SortType")
+	sortParam := this.GetString("SortParam")
+
+	var startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = paging.StartIndex(currentIndex, pageSize)
+
+	var condition string
+	var pars []interface{}
+
+	if etaBusinessIds != "" {
+		businessIdsSlice := make([]int, 0)
+		businessIds := strings.Split(etaBusinessIds, ",")
+		if len(businessIds) == 0 {
+			br.Msg = "请选择正确的商家"
+			return
+		}
+		for _, id := range businessIds {
+			if id != "" {
+				adminIdInt, e := strconv.Atoi(id)
+				if e != nil {
+					br.Msg = "请选择正确的商家"
+					return
+				}
+				businessIdsSlice = append(businessIdsSlice, adminIdInt)
+			}
+		}
+		if len(businessIdsSlice) > 0 {
+			condition += ` AND eta_business_id IN (` + utils.GetOrmInReplace(len(businessIdsSlice)) + `) `
+			pars = append(pars, businessIdsSlice)
+		}
+	}
+	if startDate != "" {
+		// 校验日期格式
+		_, err := time.ParseInLocation(utils.FormatDate, startDate, time.Local)
+		if err != nil {
+			br.Msg = "请选择正确的开始日期"
+			return
+		}
+		condition += ` AND collect_date >= ? `
+		pars = append(pars, startDate)
+	}
+	if endDate != "" {
+		// 校验日期格式
+		endTime, err := time.ParseInLocation(utils.FormatDate, endDate, time.Local)
+		if err != nil {
+			br.Msg = "请选择正确的结束日期"
+			return
+		}
+		condition += ` AND collect_date < ? `
+		nextDate := endTime.AddDate(0, 0, 1).Format(utils.FormatDate)
+		pars = append(pars, nextDate)
+	}
+	order := ""
+	if sortParam == "LastCollectChartTime" {
+		if sortType == "desc" || sortType == "asc" {
+			order = " last_collect_chart_time " + sortType
+		}
+	}
+	statObj := new(chart_collect.UserCollectChartStat)
+
+	total, err := statObj.GetGroupUserCountByCondition(condition, pars)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据总数失败,Err:" + err.Error()
+		return
+	}
+	page := paging.GetPaging(currentIndex, pageSize, total)
+	resp := new(chart_collect.UserStatListResp)
+	list := make([]*chart_collect.UserCollectChartStatItem, 0)
+	if total == 0 {
+		resp.List = list
+		resp.Paging = page
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "获取成功"
+		br.Data = resp
+		return
+	}
+	list, err = statObj.GetGroupUserPageListByCondition(condition, pars, order, startSize, pageSize)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+	// 查找商户信息
+	businessCodeStr := make([]string, 0)
+	businessCodeMap := make(map[string]string)
+	for _, v := range list {
+		businessCodeStr = append(businessCodeStr, v.BusinessCode)
+	}
+	obj := new(eta_business.EtaBusiness)
+	condition1 := " AND business_code IN (" + utils.GetOrmInReplace(len(businessCodeStr)) + ") "
+	var pars1 []interface{}
+	pars1 = append(pars1, businessCodeStr)
+	businessList, err := obj.GetItemsByCondition(condition1, pars1, []string{"business_code", "business_name"}, "")
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取商户信息失败,Err:" + err.Error()
+		return
+	}
+	for _, v := range businessList {
+		businessCodeMap[v.BusinessCode] = v.BusinessName
+	}
+
+	for _, v := range list {
+		if businessName, ok := businessCodeMap[v.BusinessCode]; ok {
+			v.BusinessName = businessName
+		}
+	}
+	resp.List = list
+	resp.Paging = page
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// ChartStat
+// @Title 图表纬度统计收藏数据
+// @Description 图表纬度统计收藏数据
+// @Success 200 {object} data_manage.EdbClassifyListResp
+// @router /stat/chart [get]
+func (this *ChartCollectStatController) ChartStat() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	statObj := new(chart_collect.ChartCollectStat)
+	resp := new(chart_collect.ChartStatListResp)
+	list, err := statObj.GetChartInfoStatusNum()
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+
+	if len(list) == 0 {
+		resp.List = make([]*chart_collect.ChartCollectChartStatItem, 0)
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "获取成功"
+		br.Data = resp
+		return
+	}
+
+	// 查询图表名称
+	chartInfoIds := make([]int, 0)
+	for _, v := range list {
+		chartInfoIds = append(chartInfoIds, v.ChartInfoId)
+	}
+	chartList, err := models.GetChartInfoListByChartIds(chartInfoIds)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取图表信息失败,Err:" + err.Error()
+		return
+	}
+	chartInfoMap := make(map[int]string)
+	for _, v := range chartList {
+		chartInfoMap[v.ChartInfoId] = v.ChartName
+	}
+	for _, v := range list {
+		if name, ok := chartInfoMap[v.ChartInfoId]; ok {
+			v.ChartName = name
+		}
+	}
+	resp.List = list
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// UserDetailList
+// @Title 用户纬度统计收藏图表列表
+// @Description 用户纬度统计收藏图表列表
+// @Success 200 {object} data_manage.EdbClassifyListResp
+// @router /stat/user/detailList [get]
+func (this *ChartCollectStatController) UserDetailList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	// 默认查一级分类和一级分类下的指标信息,
+	// 如果是 子级分类,查询该子级分类的下一级分类和指标信息
+	// 增加标识判断是文件夹还是指标列表
+	startDate := this.GetString("StartDate")
+	endDate := this.GetString("EndDate")
+	userId, _ := this.GetInt("UserId")
+	pageSize, _ := this.GetInt("PageSize")
+	currentIndex, _ := this.GetInt("CurrentIndex")
+	keyword := this.GetString("Keyword")
+
+	var startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = paging.StartIndex(currentIndex, pageSize)
+
+	var condition string
+	var pars []interface{}
+
+	if userId == 0 {
+		br.Msg = "请选择用户"
+		return
+	}
+	condition += ` AND user_id = ? `
+	pars = append(pars, userId)
+	if startDate != "" {
+		// 校验日期格式
+		_, err := time.ParseInLocation(utils.FormatDate, startDate, time.Local)
+		if err != nil {
+			br.Msg = "请选择正确的开始日期"
+			return
+		}
+		condition += ` AND a.collect_time >= ? `
+		pars = append(pars, startDate)
+	}
+	if endDate != "" {
+		// 校验日期格式
+		endTime, err := time.ParseInLocation(utils.FormatDate, endDate, time.Local)
+		if err != nil {
+			br.Msg = "请选择正确的结束日期"
+			return
+		}
+		condition += ` AND a.collect_time < ? `
+		nextDate := endTime.AddDate(0, 0, 1).Format(utils.FormatDate)
+		pars = append(pars, nextDate)
+	}
+
+	if keyword != "" {
+		// 根据空格拆分
+		keys := strings.Split(keyword, " ")
+		condition += ` AND ( `
+		for _, v := range keys {
+			if v != "" {
+				condition += ` b.chart_name like ? and`
+				pars = append(pars, "%"+v+"%")
+			}
+		}
+		condition = condition[:len(condition)-3]
+		condition += ` )`
+	}
+
+	total, err := chart_collect.GetChartCollectCountByCondition(condition, pars)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据总数失败,Err:" + err.Error()
+		return
+	}
+	page := paging.GetPaging(currentIndex, pageSize, int(total))
+	resp := new(chart_collect.ChartCollectListResp)
+	list := make([]*chart_collect.ChartCollectView, 0)
+	if total == 0 {
+		resp.List = list
+		resp.Paging = page
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "获取成功"
+		br.Data = resp
+		return
+	}
+	list, err = chart_collect.GetChartCollectPageByCondition(condition, pars, startSize, pageSize)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+
+	resp.List = list
+	resp.Paging = page
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}

+ 5 - 5
controllers/chart_info.go

@@ -521,7 +521,7 @@ func (this *ChartInfoController) ChartList() {
 	}
 
 	if len(allChartInfo) > 0 {
-		adminIds := make([]int, 0)
+		/*adminIds := make([]int, 0)
 		for _, v := range allChartInfo {
 			adminIds = append(adminIds, v.SysUserId)
 		}
@@ -530,12 +530,12 @@ func (this *ChartInfoController) ChartList() {
 			br.Msg = "获取失败"
 			br.ErrMsg = "获取机构失败,Err:" + er.Error()
 			return
-		}
+		}*/
 		for k, v := range allChartInfo {
 			v.Children = make([]*models.ChartClassifyItems, 0)
-			if n, ok := adminCompanyMap[v.SysUserId]; ok {
-				allChartInfo[k].Company = n
-			}
+			//if n, ok := adminCompanyMap[v.SysUserId]; ok {
+			allChartInfo[k].Company = utils.HzCompanyName
+			//}
 		}
 	}
 	page := paging.GetPaging(currentIndex, pageSize, int(total))

+ 68 - 0
controllers/company_industry.go

@@ -0,0 +1,68 @@
+package controllers
+
+import (
+	"eta/eta_forum_admin/models"
+	"eta/eta_forum_admin/models/company"
+	"eta/eta_forum_admin/utils"
+)
+
+// 行业管理
+type CompanyIndustryController struct {
+	BaseAuthController
+}
+
+// @Title 根据客户类型获取行业列表
+// @Description 根据客户类型获取行业列表接口
+// @Param   Classify   query   string  true       "客户类型:ficc,合作伙伴,权益"
+// @Success 200 {object} models.CompanyIndustryResp
+// @router /industry/list [get]
+func (this *CompanyIndustryController) List() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	roleCodeType := sysUser.RoleTypeCode
+
+	classify := this.GetString("Classify")
+
+	if classify == "" {
+		if roleCodeType == utils.ROLE_TYPE_CODE_FICC_ADMIN || roleCodeType == utils.ROLE_TYPE_CODE_FICC_SELLER {
+			classify = utils.COMPANY_CLASSIFY_FICC
+		} else if roleCodeType == utils.ROLE_TYPE_CODE_RAI_SELLER || roleCodeType == utils.ROLE_TYPE_CODE_RAI_ADMIN {
+			classify = utils.COMPANY_CLASSIFY_RAI
+		}
+	}
+	list, err := company.GetCompanyIndustry(classify)
+	if err != nil {
+		br.Msg = "获取行业数据失败"
+		br.ErrMsg = "获取行业数据失败,Err:" + err.Error()
+		return
+	}
+	lenList := len(list)
+	for i := 0; i < lenList; i++ {
+		child, err := company.GetCompanyIndustryChildren(classify, list[i].IndustryId)
+		if err != nil {
+			br.Msg = "获取行业数据失败"
+			br.ErrMsg = "获取行业数据失败,Err:" + err.Error()
+			return
+		}
+		list[i].Children = child
+	}
+	if lenList <= 0 {
+		list = make([]*company.CompanyIndustryItem, 0)
+	}
+	resp := new(company.CompanyIndustryResp)
+	resp.List = list
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}

+ 2190 - 0
controllers/company_seller.go

@@ -0,0 +1,2190 @@
+package controllers
+
+import (
+	"eta/eta_forum_admin/models"
+	"eta/eta_forum_admin/models/company"
+	"eta/eta_forum_admin/models/system"
+	"eta/eta_forum_admin/services"
+	"eta/eta_forum_admin/utils"
+	"fmt"
+	"strconv"
+)
+
+// 销售管理
+type CompanySellerController struct {
+	BaseAuthController
+}
+
+// RoadshowFiccList
+// @Title 路演FICC销售列表
+// @Description 路演FICC销售列表
+// @Success 200 {object} system.RoadshowGroupResp
+// @router /seller/roadshow_ficc/list [get]
+func (this *CompanySellerController) RoadshowFiccList() {
+	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
+	}
+
+	resp := new(system.RoadshowGroupResp)
+	productId := services.GetProductId(sysUser.RoleTypeCode)
+	if productId == utils.COMPANY_PRODUCT_RAI_ID {
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "获取成功"
+		br.Data = resp
+		return
+	}
+
+	// 分组
+	departmentId := 2
+	groups, e := system.GetGroupByDepartmentId(departmentId)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取FICC分组失败, Err: " + e.Error()
+		return
+	}
+	teamGroupId := make(map[int]int, 0) // 小组ID对应的大组ID
+	var list []system.RoadshowGroups
+	for _, g := range groups {
+		if g.ParentId > 0 {
+			teamGroupId[g.GroupId] = g.ParentId
+			continue
+		}
+
+		// 大组
+		list = append(list, system.RoadshowGroups{
+			GroupId:   g.GroupId,
+			GroupName: g.GroupName,
+		})
+	}
+
+	// 销售
+	roleArr := []string{
+		utils.ROLE_TYPE_CODE_FICC_ADMIN, utils.ROLE_TYPE_CODE_FICC_DEPARTMENT, utils.ROLE_TYPE_CODE_FICC_GROUP,
+		utils.ROLE_TYPE_CODE_FICC_TEAM, utils.ROLE_TYPE_CODE_FICC_SELLER,
+	}
+	cond := fmt.Sprintf(` AND role_type_code IN (%s) AND enabled = 1`, utils.GetOrmInReplace(len(roleArr)))
+	pars := make([]interface{}, 0)
+	pars = append(pars, roleArr)
+	sellers, e := system.GetSysUserItemsOrderByCreated(cond, pars)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取FICC销售失败, Err: " + e.Error()
+		return
+	}
+
+	groupIdChild := make(map[int][]system.RoadshowGroupSellers) // 大组ID对应的销售
+	for _, s := range sellers {
+		var seller system.RoadshowGroupSellers
+		seller.AdminId = s.AdminId
+		seller.AdminName = s.RealName
+
+		gid := teamGroupId[s.GroupId]
+		if gid > 0 {
+			// 小组
+			seller.GroupId = gid
+			seller.TeamId = s.GroupId
+		} else {
+			// 大组
+			seller.GroupId = s.GroupId
+		}
+		groupIdChild[seller.GroupId] = append(groupIdChild[seller.GroupId], seller)
+	}
+
+	// 根据当前角色过滤可选项
+
+	for _, v := range list {
+		// 如果是销售、组长、主管, 那么过滤掉自己大组外的其他组
+		if sysUser.RoleTypeCode == utils.ROLE_TYPE_CODE_FICC_GROUP || sysUser.RoleTypeCode == utils.ROLE_TYPE_CODE_FICC_TEAM || sysUser.RoleTypeCode == utils.ROLE_TYPE_CODE_FICC_SELLER {
+			if sysUser.GroupId != v.GroupId && teamGroupId[sysUser.GroupId] != v.GroupId {
+				continue
+			}
+		}
+		child := groupIdChild[v.GroupId]
+
+		// 如果是小组长, 那么只能看到自己小组的信息
+		if sysUser.RoleTypeCode == utils.ROLE_TYPE_CODE_FICC_TEAM {
+			var team []system.RoadshowGroupSellers
+			for _, c := range child {
+				if c.TeamId == sysUser.GroupId {
+					team = append(team, c)
+				}
+			}
+			v.Child = team
+			resp.List = append(resp.List, v)
+			continue
+		}
+
+		// 如果是普通销售, 那么只能看到分组中的自己
+		if sysUser.RoleTypeCode == utils.ROLE_TYPE_CODE_FICC_SELLER {
+			var self []system.RoadshowGroupSellers
+			for _, c := range child {
+				if c.AdminId == sysUser.AdminId {
+					self = append(self, c)
+					break
+				}
+			}
+			v.Child = self
+			resp.List = append(resp.List, v)
+			continue
+		}
+
+		// 非以上两种情况, 可见该大组所有销售
+		v.Child = child
+		resp.List = append(resp.List, v)
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// @Title 企查查模糊查询客户接口
+// @Description 企查查模糊查询客户
+// @Param   KeyWord   query   string  true       "搜索关键词"
+// @Success 200 {object} services.Result
+// @router /potential/company/qccSearch [get]
+func (this *CompanySellerController) CompanyQCCSearch() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var err error
+	var results []services.Result
+	keyWord := this.GetString("KeyWord")
+	if keyWord != "" {
+		results, err = services.QCCFuzzySearch(keyWord)
+		if err != nil {
+			br.Data = results
+			br.Msg = "获取数据失败," + err.Error()
+			br.Ret = 200
+			br.Success = true
+			return
+		}
+	}
+	br.Data = results
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// CheckListV2
+// @Title 获取组长/组员下销售(根据部门、分组)
+// @Description 获取组长/组员下销售(根据部门、分组)接口
+// @Param   AllSeller   query   bool  true       "是否获取部门所有的销售信息"
+// @Param   Status   query   int  true       "客户状态 1:流失 0:非流失"
+// @Param   AllEnabled   query   bool  true       "是否获取包含禁用的用户"
+// @Success 200 {object} company.DepartmentGroupSellersResp
+// @router /seller/check/listV2 [get]
+func (this *CompanySellerController) CheckListV2() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	roleCodeTypeStr := ``
+	productId := services.GetProductId(sysUser.RoleTypeCode)
+	switch productId {
+	case 1:
+		roleCodeTypeStr = `"` + utils.ROLE_TYPE_CODE_FICC_SELLER + `","` + utils.ROLE_TYPE_CODE_FICC_GROUP + `","` + utils.ROLE_TYPE_CODE_FICC_TEAM + `"`
+	case 2:
+		roleCodeTypeStr = `"` + utils.ROLE_TYPE_CODE_RAI_SELLER + `","` + utils.ROLE_TYPE_CODE_RAI_GROUP + `"`
+	}
+
+	// ficc管理员,不要只查销售
+	if sysUser.RoleTypeCode == utils.ROLE_TYPE_CODE_FICC_ADMIN {
+		roleCodeTypeStr = ``
+	} else if sysUser.RoleTypeCode == utils.ROLE_TYPE_CODE_RAI_ADMIN {
+		// 权益管理员,查看所有权益的账号
+		roleCodeTypeStr = `"` + utils.ROLE_TYPE_CODE_RAI_SELLER + `","` + utils.ROLE_TYPE_CODE_RAI_GROUP + `","` + utils.ROLE_TYPE_CODE_RAI_RESEARCHR + `","` + utils.ROLE_TYPE_CODE_RAI_ADMIN + `"`
+	}
+
+	getAllSeller, _ := this.GetBool("AllSeller", false)
+	getAllUser, _ := this.GetBool("AllUser", false)
+	status, _ := this.GetInt("Status", 0)
+	getAllEnabled, _ := this.GetBool("AllEnabled", false)
+	enabled := -1      //默认只获取正常状态的用户
+	if getAllEnabled { //获取所有状态的用户
+		enabled = -1
+	}
+	list := make([]company.DepartmentGroupSellers, 0)
+	if getAllUser {
+		departmentList, err := system.GetDepartmentList()
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取部门失败,Err:" + err.Error()
+			return
+		}
+		for _, department := range departmentList {
+			departmentGroupSellerList := make([]company.DepartmentGroupSellers, 0)
+			item := company.DepartmentGroupSellers{
+				AdminId: fmt.Sprint("department_", department.DepartmentId),
+				//AdminName string `description:"系统用户名称"`
+				RealName:     department.DepartmentName,
+				ChildrenList: departmentGroupSellerList,
+			}
+
+			groupList, err := system.GetSysGroupByDepartmentId(department.DepartmentId)
+			if err != nil {
+				br.Msg = "获取失败"
+				br.ErrMsg = "获取分组失败,Err:" + err.Error()
+				return
+			}
+			for _, group := range groupList {
+				groupSellerList := make([]company.DepartmentGroupSellers, 0)
+				groupItem := company.DepartmentGroupSellers{
+					AdminId: fmt.Sprint("group_", group.DepartmentId),
+					//AdminName string `description:"系统用户名称"`
+					RealName:     group.GroupName,
+					ChildrenList: groupSellerList,
+				}
+				teamList, err := system.GetSysTeamByDepartmentId(group.GroupId)
+				if err != nil {
+					br.Msg = "获取小组失败"
+					br.ErrMsg = "获取小组失败,Err:" + err.Error()
+					return
+				}
+				for _, team := range teamList {
+					teamSellerList := make([]company.DepartmentGroupSellers, 0)
+					teamItem := company.DepartmentGroupSellers{
+						AdminId: fmt.Sprint("team_", team.GroupId),
+						//AdminName string `description:"系统用户名称"`
+						RealName:     team.GroupName,
+						ChildrenList: teamSellerList,
+					}
+					sellerList, err := system.GetTeamSysUserList(team.GroupId, enabled)
+					if err != nil {
+						br.Msg = "获取销售失败"
+						br.ErrMsg = "获取销售失败,Err:" + err.Error()
+						return
+					}
+					for _, seller := range sellerList {
+						sellerItem := company.DepartmentGroupSellers{
+							AdminId: fmt.Sprint(seller.AdminId),
+							//AdminName string `description:"系统用户名称"`
+							RealName:     seller.RealName,
+							Authority:    seller.Authority,
+							RoleTypeCode: seller.RoleTypeCode,
+						}
+						//fmt.Println("seller.RealName:", seller.RealName)
+						teamSellerList = append(teamSellerList, sellerItem)
+					}
+					teamSellerList = sortDepartmentGroupSellers(teamSellerList)
+					teamItem.ChildrenList = teamSellerList
+					if len(teamSellerList) > 0 {
+						groupSellerList = append(groupSellerList, teamItem)
+					}
+				}
+				//分组为0的销售(直属分组)
+				groupTeamSellerList, err := system.GetGrooupsysUserList(group.GroupId, roleCodeTypeStr, enabled)
+				if err != nil {
+					br.Msg = "获取销售失败"
+					br.ErrMsg = "获取销售失败,Err:" + err.Error()
+					return
+				}
+				if len(groupTeamSellerList) > 0 {
+					tmpDepartmentGroupSellersList := make([]company.DepartmentGroupSellers, 0)
+					for _, seller := range groupTeamSellerList {
+						sellerItem := company.DepartmentGroupSellers{
+							AdminId: fmt.Sprint(seller.AdminId),
+							//AdminName string `description:"系统用户名称"`
+							RealName:     seller.RealName,
+							Authority:    seller.Authority,
+							RoleTypeCode: seller.RoleTypeCode,
+						}
+						tmpDepartmentGroupSellersList = append(tmpDepartmentGroupSellersList, sellerItem)
+					}
+					//排个序
+					tmpDepartmentGroupSellersList = sortDepartmentGroupSellers(tmpDepartmentGroupSellersList)
+					groupSellerList = append(groupSellerList, tmpDepartmentGroupSellersList...)
+				}
+
+				//groupItem.ChildrenList = groupSellerList
+				//if len(groupSellerList) > 0 {
+				//	list = append(list, item)
+				//}
+
+				//sellerList, err := system.GetGroupSysUserList(group.DepartmentId)
+				//if err != nil {
+				//	br.Msg = "获取销售失败"
+				//	br.ErrMsg = "获取销售失败,Err:" + err.Error()
+				//	return
+				//}
+				//for _, seller := range sellerList {
+				//	sellerItem := company.DepartmentGroupSellers{
+				//		AdminId: fmt.Sprint(seller.AdminId),
+				//		//AdminName string `description:"系统用户名称"`
+				//		RealName:     seller.RealName,
+				//		Authority:    seller.Authority,
+				//		RoleTypeCode: seller.RoleTypeCode,
+				//	}
+				//	groupSellerList = append(groupSellerList, sellerItem)
+				//}
+				//groupSellerList = sortDepartmentGroupSellers(groupSellerList)
+				groupItem.ChildrenList = groupSellerList
+				if len(groupSellerList) > 0 {
+					departmentGroupSellerList = append(departmentGroupSellerList, groupItem)
+				}
+			}
+
+			//分组为0的销售(直属部门)
+			departmentSellerList, err := system.GetDepartmentGroupSysUserList(department.DepartmentId, 0, roleCodeTypeStr)
+			if err != nil {
+				br.Msg = "获取销售失败"
+				br.ErrMsg = "获取销售失败,Err:" + err.Error()
+				return
+			}
+			if len(departmentSellerList) > 0 {
+				tmpDepartmentGroupSellersList := make([]company.DepartmentGroupSellers, 0)
+				for _, seller := range departmentSellerList {
+					sellerItem := company.DepartmentGroupSellers{
+						AdminId: fmt.Sprint(seller.AdminId),
+						//AdminName string `description:"系统用户名称"`
+						RealName:     seller.RealName,
+						Authority:    seller.Authority,
+						RoleTypeCode: seller.RoleTypeCode,
+					}
+					tmpDepartmentGroupSellersList = append(tmpDepartmentGroupSellersList, sellerItem)
+				}
+				//排个序
+				tmpDepartmentGroupSellersList = sortDepartmentGroupSellers(tmpDepartmentGroupSellersList)
+				departmentGroupSellerList = append(departmentGroupSellerList, tmpDepartmentGroupSellersList...)
+			}
+
+			item.ChildrenList = departmentGroupSellerList
+			if len(departmentGroupSellerList) > 0 {
+				list = append(list, item)
+			}
+		}
+
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "获取成功"
+		br.Data = company.DepartmentGroupSellersResp{
+			List: list,
+		}
+
+		return
+	}
+	if getAllSeller == false {
+		//超管看部门、小组、销售
+		if sysUser.RoleTypeCode == utils.ROLE_TYPE_CODE_ADMIN {
+			departmentList, err := system.GetDepartmentList()
+			if err != nil {
+				br.Msg = "获取失败"
+				br.ErrMsg = "获取部门失败,Err:" + err.Error()
+				return
+			}
+			for _, department := range departmentList {
+				departmentGroupSellerList := make([]company.DepartmentGroupSellers, 0)
+				item := company.DepartmentGroupSellers{
+					AdminId: fmt.Sprint("department_", department.DepartmentId),
+					//AdminName string `description:"系统用户名称"`
+					RealName:     department.DepartmentName,
+					ChildrenList: departmentGroupSellerList,
+				}
+
+				groupList, err := system.GetSysGroupByDepartmentId(department.DepartmentId)
+				if err != nil {
+					br.Msg = "获取失败"
+					br.ErrMsg = "获取分组失败,Err:" + err.Error()
+					return
+				}
+				for _, group := range groupList {
+					groupSellerList := make([]company.DepartmentGroupSellers, 0)
+					groupItem := company.DepartmentGroupSellers{
+						AdminId: fmt.Sprint("group_", group.DepartmentId),
+						//AdminName string `description:"系统用户名称"`
+						RealName:     group.GroupName,
+						ChildrenList: groupSellerList,
+					}
+					teamList, err := system.GetSysTeamByDepartmentId(group.GroupId)
+					if err != nil {
+						br.Msg = "获取小组失败"
+						br.ErrMsg = "获取小组失败,Err:" + err.Error()
+						return
+					}
+					for _, team := range teamList {
+						teamSellerList := make([]company.DepartmentGroupSellers, 0)
+						teamItem := company.DepartmentGroupSellers{
+							AdminId: fmt.Sprint("team_", team.GroupId),
+							//AdminName string `description:"系统用户名称"`
+							RealName:     team.GroupName,
+							ChildrenList: teamSellerList,
+						}
+						sellerList, err := system.GetTeamSysUserList(team.GroupId, enabled)
+						if err != nil {
+							br.Msg = "获取销售失败"
+							br.ErrMsg = "获取销售失败,Err:" + err.Error()
+							return
+						}
+						for _, seller := range sellerList {
+							sellerItem := company.DepartmentGroupSellers{
+								AdminId: fmt.Sprint(seller.AdminId),
+								//AdminName string `description:"系统用户名称"`
+								RealName:     seller.RealName,
+								Authority:    seller.Authority,
+								RoleTypeCode: seller.RoleTypeCode,
+							}
+							//fmt.Println("seller.RealName:", seller.RealName)
+							teamSellerList = append(teamSellerList, sellerItem)
+						}
+						teamSellerList = sortDepartmentGroupSellers(teamSellerList)
+						teamItem.ChildrenList = teamSellerList
+						if len(teamSellerList) > 0 {
+							groupSellerList = append(groupSellerList, teamItem)
+						}
+					}
+					//分组为0的销售(直属分组)
+					groupTeamSellerList, err := system.GetGrooupsysUserList(group.GroupId, roleCodeTypeStr, enabled)
+					if err != nil {
+						br.Msg = "获取销售失败"
+						br.ErrMsg = "获取销售失败,Err:" + err.Error()
+						return
+					}
+					if len(groupTeamSellerList) > 0 {
+						tmpDepartmentGroupSellersList := make([]company.DepartmentGroupSellers, 0)
+						for _, seller := range groupTeamSellerList {
+							sellerItem := company.DepartmentGroupSellers{
+								AdminId: fmt.Sprint(seller.AdminId),
+								//AdminName string `description:"系统用户名称"`
+								RealName:     seller.RealName,
+								Authority:    seller.Authority,
+								RoleTypeCode: seller.RoleTypeCode,
+							}
+							tmpDepartmentGroupSellersList = append(tmpDepartmentGroupSellersList, sellerItem)
+						}
+						//排个序
+						tmpDepartmentGroupSellersList = sortDepartmentGroupSellers(tmpDepartmentGroupSellersList)
+						groupSellerList = append(groupSellerList, tmpDepartmentGroupSellersList...)
+					}
+
+					//groupItem.ChildrenList = groupSellerList
+					//if len(groupSellerList) > 0 {
+					//	list = append(list, item)
+					//}
+
+					//sellerList, err := system.GetGroupSysUserList(group.DepartmentId)
+					//if err != nil {
+					//	br.Msg = "获取销售失败"
+					//	br.ErrMsg = "获取销售失败,Err:" + err.Error()
+					//	return
+					//}
+					//for _, seller := range sellerList {
+					//	sellerItem := company.DepartmentGroupSellers{
+					//		AdminId: fmt.Sprint(seller.AdminId),
+					//		//AdminName string `description:"系统用户名称"`
+					//		RealName:     seller.RealName,
+					//		Authority:    seller.Authority,
+					//		RoleTypeCode: seller.RoleTypeCode,
+					//	}
+					//	groupSellerList = append(groupSellerList, sellerItem)
+					//}
+					//groupSellerList = sortDepartmentGroupSellers(groupSellerList)
+					groupItem.ChildrenList = groupSellerList
+					if len(groupSellerList) > 0 {
+						departmentGroupSellerList = append(departmentGroupSellerList, groupItem)
+					}
+				}
+
+				//分组为0的销售(直属部门)
+				departmentSellerList, err := system.GetDepartmentGroupSysUserList(department.DepartmentId, 0, roleCodeTypeStr)
+				if err != nil {
+					br.Msg = "获取销售失败"
+					br.ErrMsg = "获取销售失败,Err:" + err.Error()
+					return
+				}
+				if len(departmentSellerList) > 0 {
+					tmpDepartmentGroupSellersList := make([]company.DepartmentGroupSellers, 0)
+					for _, seller := range departmentSellerList {
+						sellerItem := company.DepartmentGroupSellers{
+							AdminId: fmt.Sprint(seller.AdminId),
+							//AdminName string `description:"系统用户名称"`
+							RealName:     seller.RealName,
+							Authority:    seller.Authority,
+							RoleTypeCode: seller.RoleTypeCode,
+						}
+						tmpDepartmentGroupSellersList = append(tmpDepartmentGroupSellersList, sellerItem)
+					}
+					//排个序
+					tmpDepartmentGroupSellersList = sortDepartmentGroupSellers(tmpDepartmentGroupSellersList)
+					departmentGroupSellerList = append(departmentGroupSellerList, tmpDepartmentGroupSellersList...)
+				}
+
+				item.ChildrenList = departmentGroupSellerList
+				if len(departmentGroupSellerList) > 0 {
+					list = append(list, item)
+				}
+			}
+		} else if sysUser.RoleTypeCode == utils.ROLE_TYPE_CODE_FICC_TEAM || sysUser.Authority == 4 {
+			//销售组长看销售
+			if status == 0 {
+				directorId := sysUser.GroupId
+				fmt.Println("directorId:", directorId)
+				groupList, err := system.GetSysGroupByDirectorId(directorId)
+				if err != nil {
+					br.Msg = "获取失败"
+					br.ErrMsg = "获取分组失败,Err:" + err.Error()
+					return
+				}
+				for _, group := range groupList {
+					groupSellerList := make([]company.DepartmentGroupSellers, 0)
+					groupItem := company.DepartmentGroupSellers{
+						AdminId: fmt.Sprint("group_", group.DepartmentId),
+						//AdminName string `description:"系统用户名称"`
+						RealName:     group.GroupName,
+						ChildrenList: groupSellerList,
+					}
+					teamList, err := system.GetSysTeamByDepartmentId(group.GroupId)
+					if err != nil {
+						br.Msg = "获取小组失败"
+						br.ErrMsg = "获取小组失败,Err:" + err.Error()
+						return
+					}
+					for _, team := range teamList {
+						teamSellerList := make([]company.DepartmentGroupSellers, 0)
+						teamItem := company.DepartmentGroupSellers{
+							AdminId: fmt.Sprint("team_", team.GroupId),
+							//AdminName string `description:"系统用户名称"`
+							RealName:     team.GroupName,
+							ChildrenList: teamSellerList,
+						}
+						sellerList, err := system.GetTeamSysUserList(team.GroupId, enabled)
+						if err != nil {
+							br.Msg = "获取销售失败"
+							br.ErrMsg = "获取销售失败,Err:" + err.Error()
+							return
+						}
+						for _, seller := range sellerList {
+							sellerItem := company.DepartmentGroupSellers{
+								AdminId: fmt.Sprint(seller.AdminId),
+								//AdminName string `description:"系统用户名称"`
+								RealName:     seller.RealName,
+								Authority:    seller.Authority,
+								RoleTypeCode: seller.RoleTypeCode,
+							}
+							//fmt.Println("seller.RealName:", seller.RealName)
+							teamSellerList = append(teamSellerList, sellerItem)
+						}
+						teamSellerList = sortDepartmentGroupSellers(teamSellerList)
+						teamItem.ChildrenList = teamSellerList
+						if len(teamSellerList) > 0 {
+							groupSellerList = append(groupSellerList, teamItem)
+						}
+					}
+					//分组为0的销售(直属分组)
+					groupTeamSellerList, err := system.GetGrooupsysUserList(group.GroupId, roleCodeTypeStr, enabled)
+					if err != nil {
+						br.Msg = "获取销售失败"
+						br.ErrMsg = "获取销售失败,Err:" + err.Error()
+						return
+					}
+					if len(groupTeamSellerList) > 0 {
+						tmpDepartmentGroupSellersList := make([]company.DepartmentGroupSellers, 0)
+						for _, seller := range groupTeamSellerList {
+							sellerItem := company.DepartmentGroupSellers{
+								AdminId: fmt.Sprint(seller.AdminId),
+								//AdminName string `description:"系统用户名称"`
+								RealName:     seller.RealName,
+								Authority:    seller.Authority,
+								RoleTypeCode: seller.RoleTypeCode,
+							}
+							tmpDepartmentGroupSellersList = append(tmpDepartmentGroupSellersList, sellerItem)
+						}
+						//排个序
+						tmpDepartmentGroupSellersList = sortDepartmentGroupSellers(tmpDepartmentGroupSellersList)
+						groupSellerList = append(groupSellerList, tmpDepartmentGroupSellersList...)
+					}
+
+					groupItem.ChildrenList = groupSellerList
+					if len(groupSellerList) > 0 {
+						list = append(list, groupItem)
+					}
+				}
+			} else {
+				departmentStr := `1,2`
+				departmentList, err := system.GetDepartmentListByIds(departmentStr)
+				if err != nil {
+					br.Msg = "获取失败"
+					br.ErrMsg = "获取部门失败,Err:" + err.Error()
+					return
+				}
+				for _, department := range departmentList {
+					departmentGroupSellerList := make([]company.DepartmentGroupSellers, 0)
+					item := company.DepartmentGroupSellers{
+						AdminId: fmt.Sprint("department_", department.DepartmentId),
+						//AdminName string `description:"系统用户名称"`
+						RealName:     department.DepartmentName,
+						ChildrenList: departmentGroupSellerList,
+					}
+
+					groupList, err := system.GetSysGroupByDepartmentId(department.DepartmentId)
+					if err != nil {
+						br.Msg = "获取失败"
+						br.ErrMsg = "获取分组失败,Err:" + err.Error()
+						return
+					}
+					for _, group := range groupList {
+						groupSellerList := make([]company.DepartmentGroupSellers, 0)
+						groupItem := company.DepartmentGroupSellers{
+							AdminId: fmt.Sprint("group_", group.DepartmentId),
+							//AdminName string `description:"系统用户名称"`
+							RealName:     group.GroupName,
+							ChildrenList: groupSellerList,
+						}
+						teamList, err := system.GetSysTeamByDepartmentId(group.GroupId)
+						if err != nil {
+							br.Msg = "获取小组失败"
+							br.ErrMsg = "获取小组失败,Err:" + err.Error()
+							return
+						}
+						for _, team := range teamList {
+							teamSellerList := make([]company.DepartmentGroupSellers, 0)
+							teamItem := company.DepartmentGroupSellers{
+								AdminId: fmt.Sprint("team_", team.GroupId),
+								//AdminName string `description:"系统用户名称"`
+								RealName:     team.GroupName,
+								ChildrenList: teamSellerList,
+							}
+							sellerList, err := system.GetTeamSysUserList(team.GroupId, enabled)
+							if err != nil {
+								br.Msg = "获取销售失败"
+								br.ErrMsg = "获取销售失败,Err:" + err.Error()
+								return
+							}
+							for _, seller := range sellerList {
+								sellerItem := company.DepartmentGroupSellers{
+									AdminId: fmt.Sprint(seller.AdminId),
+									//AdminName string `description:"系统用户名称"`
+									RealName:     seller.RealName,
+									Authority:    seller.Authority,
+									RoleTypeCode: seller.RoleTypeCode,
+								}
+								//fmt.Println("seller.RealName:", seller.RealName)
+								teamSellerList = append(teamSellerList, sellerItem)
+							}
+							teamSellerList = sortDepartmentGroupSellers(teamSellerList)
+							teamItem.ChildrenList = teamSellerList
+							if len(teamSellerList) > 0 {
+								groupSellerList = append(groupSellerList, teamItem)
+							}
+						}
+						//分组为0的人,不光是销售(直属分组)
+						groupTeamSellerList, err := system.GetGroupSysUserList(group.GroupId)
+						if err != nil {
+							br.Msg = "获取销售失败"
+							br.ErrMsg = "获取销售失败,Err:" + err.Error()
+							return
+						}
+						if len(groupTeamSellerList) > 0 {
+							tmpDepartmentGroupSellersList := make([]company.DepartmentGroupSellers, 0)
+							for _, seller := range groupTeamSellerList {
+								sellerItem := company.DepartmentGroupSellers{
+									AdminId: fmt.Sprint(seller.AdminId),
+									//AdminName string `description:"系统用户名称"`
+									RealName:     seller.RealName,
+									Authority:    seller.Authority,
+									RoleTypeCode: seller.RoleTypeCode,
+								}
+								tmpDepartmentGroupSellersList = append(tmpDepartmentGroupSellersList, sellerItem)
+							}
+							//排个序
+							tmpDepartmentGroupSellersList = sortDepartmentGroupSellers(tmpDepartmentGroupSellersList)
+							groupSellerList = append(groupSellerList, tmpDepartmentGroupSellersList...)
+						}
+
+						//groupItem.ChildrenList = groupSellerList
+						//if len(groupSellerList) > 0 {
+						//	list = append(list, item)
+						//}
+
+						//sellerList, err := system.GetGroupSysUserList(group.DepartmentId)
+						//if err != nil {
+						//	br.Msg = "获取销售失败"
+						//	br.ErrMsg = "获取销售失败,Err:" + err.Error()
+						//	return
+						//}
+						//for _, seller := range sellerList {
+						//	sellerItem := company.DepartmentGroupSellers{
+						//		AdminId: fmt.Sprint(seller.AdminId),
+						//		//AdminName string `description:"系统用户名称"`
+						//		RealName:     seller.RealName,
+						//		Authority:    seller.Authority,
+						//		RoleTypeCode: seller.RoleTypeCode,
+						//	}
+						//	groupSellerList = append(groupSellerList, sellerItem)
+						//}
+						//groupSellerList = sortDepartmentGroupSellers(groupSellerList)
+						groupItem.ChildrenList = groupSellerList
+						if len(groupSellerList) > 0 {
+							departmentGroupSellerList = append(departmentGroupSellerList, groupItem)
+						}
+					}
+
+					//分组为0的人,不光是销售(直属部门)
+					departmentSellerList, err := system.GetDepartmentGroupSysUserList(department.DepartmentId, 0, "")
+					if err != nil {
+						br.Msg = "获取销售失败"
+						br.ErrMsg = "获取销售失败,Err:" + err.Error()
+						return
+					}
+					if len(departmentSellerList) > 0 {
+						tmpDepartmentGroupSellersList := make([]company.DepartmentGroupSellers, 0)
+						for _, seller := range departmentSellerList {
+							sellerItem := company.DepartmentGroupSellers{
+								AdminId: fmt.Sprint(seller.AdminId),
+								//AdminName string `description:"系统用户名称"`
+								RealName:     seller.RealName,
+								Authority:    seller.Authority,
+								RoleTypeCode: seller.RoleTypeCode,
+							}
+							tmpDepartmentGroupSellersList = append(tmpDepartmentGroupSellersList, sellerItem)
+						}
+						//排个序
+						tmpDepartmentGroupSellersList = sortDepartmentGroupSellers(tmpDepartmentGroupSellersList)
+						departmentGroupSellerList = append(departmentGroupSellerList, tmpDepartmentGroupSellersList...)
+					}
+
+					item.ChildrenList = departmentGroupSellerList
+					if len(departmentGroupSellerList) > 0 {
+						list = append(list, item)
+					}
+				}
+			}
+		} else if sysUser.RoleTypeCode == utils.ROLE_TYPE_CODE_COMPLIANCE || sysUser.RoleTypeCode == utils.ROLE_TYPE_CODE_FINANCE || sysUser.RoleTypeCode == utils.ROLE_TYPE_CODE_FICC_ADMIN { //合规或财务或ficc管理员
+			departmentStr := `2,5,7` //合规
+			switch sysUser.RoleTypeCode {
+			case utils.ROLE_TYPE_CODE_FINANCE: //财务
+				departmentStr = `2,5,8`
+			case utils.ROLE_TYPE_CODE_FICC_ADMIN: //ficc管理员
+				departmentStr = utils.GetFiccAdminDepartmentIds()
+			}
+			departmentList, err := system.GetDepartmentListByIds(departmentStr)
+			if err != nil {
+				br.Msg = "获取失败"
+				br.ErrMsg = "获取部门失败,Err:" + err.Error()
+				return
+			}
+			for _, department := range departmentList {
+				fmt.Println(department)
+				departmentGroupSellerList := make([]company.DepartmentGroupSellers, 0)
+				item := company.DepartmentGroupSellers{
+					AdminId: fmt.Sprint("department_", department.DepartmentId),
+					//AdminName string `description:"系统用户名称"`
+					RealName:     department.DepartmentName,
+					ChildrenList: departmentGroupSellerList,
+				}
+
+				groupList, err := system.GetSysGroupByDepartmentId(department.DepartmentId)
+				if err != nil {
+					br.Msg = "获取失败"
+					br.ErrMsg = "获取分组失败,Err:" + err.Error()
+					return
+				}
+				for _, group := range groupList {
+					groupSellerList := make([]company.DepartmentGroupSellers, 0)
+					groupItem := company.DepartmentGroupSellers{
+						AdminId: fmt.Sprint("group_", group.DepartmentId),
+						//AdminName string `description:"系统用户名称"`
+						RealName:     group.GroupName,
+						ChildrenList: groupSellerList,
+					}
+					teamList, err := system.GetSysTeamByDepartmentId(group.GroupId)
+					if err != nil {
+						br.Msg = "获取小组失败"
+						br.ErrMsg = "获取小组失败,Err:" + err.Error()
+						return
+					}
+					for _, team := range teamList {
+						teamSellerList := make([]company.DepartmentGroupSellers, 0)
+						teamItem := company.DepartmentGroupSellers{
+							AdminId: fmt.Sprint("team_", team.GroupId),
+							//AdminName string `description:"系统用户名称"`
+							RealName:     team.GroupName,
+							ChildrenList: teamSellerList,
+						}
+						sellerList, err := system.GetTeamSysUserList(team.GroupId, enabled)
+						if err != nil {
+							br.Msg = "获取销售失败"
+							br.ErrMsg = "获取销售失败,Err:" + err.Error()
+							return
+						}
+						for _, seller := range sellerList {
+							sellerItem := company.DepartmentGroupSellers{
+								AdminId: fmt.Sprint(seller.AdminId),
+								//AdminName string `description:"系统用户名称"`
+								RealName:     seller.RealName,
+								Authority:    seller.Authority,
+								RoleTypeCode: seller.RoleTypeCode,
+							}
+							//fmt.Println("seller.RealName:", seller.RealName)
+							teamSellerList = append(teamSellerList, sellerItem)
+						}
+						teamSellerList = sortDepartmentGroupSellers(teamSellerList)
+						teamItem.ChildrenList = teamSellerList
+						if len(teamSellerList) > 0 {
+							groupSellerList = append(groupSellerList, teamItem)
+						}
+					}
+					//分组为0的销售(直属分组)
+					groupTeamSellerList, err := system.GetGrooupsysUserList(group.GroupId, roleCodeTypeStr, enabled)
+					if err != nil {
+						br.Msg = "获取销售失败"
+						br.ErrMsg = "获取销售失败,Err:" + err.Error()
+						return
+					}
+					if len(groupTeamSellerList) > 0 {
+						tmpDepartmentGroupSellersList := make([]company.DepartmentGroupSellers, 0)
+						for _, seller := range groupTeamSellerList {
+							sellerItem := company.DepartmentGroupSellers{
+								AdminId: fmt.Sprint(seller.AdminId),
+								//AdminName string `description:"系统用户名称"`
+								RealName:     seller.RealName,
+								Authority:    seller.Authority,
+								RoleTypeCode: seller.RoleTypeCode,
+							}
+							tmpDepartmentGroupSellersList = append(tmpDepartmentGroupSellersList, sellerItem)
+						}
+						//排个序
+						tmpDepartmentGroupSellersList = sortDepartmentGroupSellers(tmpDepartmentGroupSellersList)
+						groupSellerList = append(groupSellerList, tmpDepartmentGroupSellersList...)
+					}
+
+					//groupItem.ChildrenList = groupSellerList
+					//if len(groupSellerList) > 0 {
+					//	list = append(list, item)
+					//}
+
+					//sellerList, err := system.GetGroupSysUserList(group.DepartmentId)
+					//if err != nil {
+					//	br.Msg = "获取销售失败"
+					//	br.ErrMsg = "获取销售失败,Err:" + err.Error()
+					//	return
+					//}
+					//for _, seller := range sellerList {
+					//	sellerItem := company.DepartmentGroupSellers{
+					//		AdminId: fmt.Sprint(seller.AdminId),
+					//		//AdminName string `description:"系统用户名称"`
+					//		RealName:     seller.RealName,
+					//		Authority:    seller.Authority,
+					//		RoleTypeCode: seller.RoleTypeCode,
+					//	}
+					//	groupSellerList = append(groupSellerList, sellerItem)
+					//}
+					//groupSellerList = sortDepartmentGroupSellers(groupSellerList)
+					groupItem.ChildrenList = groupSellerList
+					if len(groupSellerList) > 0 {
+						departmentGroupSellerList = append(departmentGroupSellerList, groupItem)
+					}
+				}
+
+				//分组为0的销售(直属部门)
+				departmentSellerList, err := system.GetDepartmentGroupSysUserList(department.DepartmentId, 0, roleCodeTypeStr)
+				if err != nil {
+					br.Msg = "获取销售失败"
+					br.ErrMsg = "获取销售失败,Err:" + err.Error()
+					return
+				}
+				if len(departmentSellerList) > 0 {
+					tmpDepartmentGroupSellerList := make([]company.DepartmentGroupSellers, 0)
+					for _, seller := range departmentSellerList {
+						sellerItem := company.DepartmentGroupSellers{
+							AdminId: fmt.Sprint(seller.AdminId),
+							//AdminName string `description:"系统用户名称"`
+							RealName:     seller.RealName,
+							Authority:    seller.Authority,
+							RoleTypeCode: seller.RoleTypeCode,
+						}
+						tmpDepartmentGroupSellerList = append(tmpDepartmentGroupSellerList, sellerItem)
+					}
+					//排个序
+					tmpDepartmentGroupSellerList = sortDepartmentGroupSellers(tmpDepartmentGroupSellerList)
+					departmentGroupSellerList = append(departmentGroupSellerList, tmpDepartmentGroupSellerList...)
+				}
+
+				item.ChildrenList = departmentGroupSellerList
+				if len(departmentGroupSellerList) > 0 {
+					list = append(list, item)
+				}
+			}
+		} else if sysUser.RoleTypeCode == utils.ROLE_TYPE_CODE_RAI_ADMIN || sysUser.Authority == 1 {
+			//部门管理员看小组、销售
+			departmentId := sysUser.DepartmentId
+			switch sysUser.RoleTypeCode {
+			case utils.ROLE_TYPE_CODE_RAI_ADMIN: //权益管理员
+				departmentId = 5
+			case utils.ROLE_TYPE_CODE_FICC_ADMIN: //ficc管理员
+				departmentId = 2
+			}
+			groupList, err := system.GetSysGroupByDepartmentId(departmentId)
+			if err != nil {
+				br.Msg = "获取失败"
+				br.ErrMsg = "获取分组失败,Err:" + err.Error()
+				return
+			}
+			for _, group := range groupList {
+				groupSellerList := make([]company.DepartmentGroupSellers, 0)
+				groupItem := company.DepartmentGroupSellers{
+					AdminId: fmt.Sprint("group_", group.DepartmentId),
+					//AdminName string `description:"系统用户名称"`
+					RealName:     group.GroupName,
+					ChildrenList: groupSellerList,
+				}
+				teamList, err := system.GetSysTeamByDepartmentId(group.GroupId)
+				if err != nil {
+					br.Msg = "获取小组失败"
+					br.ErrMsg = "获取小组失败,Err:" + err.Error()
+					return
+				}
+				for _, team := range teamList {
+					teamSellerList := make([]company.DepartmentGroupSellers, 0)
+					teamItem := company.DepartmentGroupSellers{
+						AdminId: fmt.Sprint("team_", team.GroupId),
+						//AdminName string `description:"系统用户名称"`
+						RealName:     team.GroupName,
+						ChildrenList: teamSellerList,
+					}
+					sellerList, err := system.GetTeamSysUserList(team.GroupId, enabled)
+					if err != nil {
+						br.Msg = "获取销售失败"
+						br.ErrMsg = "获取销售失败,Err:" + err.Error()
+						return
+					}
+					for _, seller := range sellerList {
+						sellerItem := company.DepartmentGroupSellers{
+							AdminId: fmt.Sprint(seller.AdminId),
+							//AdminName string `description:"系统用户名称"`
+							RealName:     seller.RealName,
+							Authority:    seller.Authority,
+							RoleTypeCode: seller.RoleTypeCode,
+						}
+						//fmt.Println("seller.RealName:", seller.RealName)
+						teamSellerList = append(teamSellerList, sellerItem)
+					}
+					teamSellerList = sortDepartmentGroupSellers(teamSellerList)
+					teamItem.ChildrenList = teamSellerList
+					if len(teamSellerList) > 0 {
+						groupSellerList = append(groupSellerList, teamItem)
+					}
+				}
+				//分组为0的销售(直属分组)
+				groupTeamSellerList, err := system.GetGrooupsysUserList(group.GroupId, roleCodeTypeStr, enabled)
+				if err != nil {
+					br.Msg = "获取销售失败"
+					br.ErrMsg = "获取销售失败,Err:" + err.Error()
+					return
+				}
+				if len(groupTeamSellerList) > 0 {
+					tmpDepartmentGroupSellersList := make([]company.DepartmentGroupSellers, 0)
+					for _, seller := range groupTeamSellerList {
+						sellerItem := company.DepartmentGroupSellers{
+							AdminId: fmt.Sprint(seller.AdminId),
+							//AdminName string `description:"系统用户名称"`
+							RealName:     seller.RealName,
+							Authority:    seller.Authority,
+							RoleTypeCode: seller.RoleTypeCode,
+						}
+						tmpDepartmentGroupSellersList = append(tmpDepartmentGroupSellersList, sellerItem)
+					}
+					//排个序
+					tmpDepartmentGroupSellersList = sortDepartmentGroupSellers(tmpDepartmentGroupSellersList)
+					groupSellerList = append(groupSellerList, tmpDepartmentGroupSellersList...)
+				}
+
+				//groupItem.ChildrenList = groupSellerList
+				//if len(groupSellerList) > 0 {
+				//	list = append(list, item)
+				//}
+
+				//sellerList, err := system.GetGroupSysUserList(group.DepartmentId)
+				//if err != nil {
+				//	br.Msg = "获取销售失败"
+				//	br.ErrMsg = "获取销售失败,Err:" + err.Error()
+				//	return
+				//}
+				//for _, seller := range sellerList {
+				//	sellerItem := company.DepartmentGroupSellers{
+				//		AdminId: fmt.Sprint(seller.AdminId),
+				//		//AdminName string `description:"系统用户名称"`
+				//		RealName:     seller.RealName,
+				//		Authority:    seller.Authority,
+				//		RoleTypeCode: seller.RoleTypeCode,
+				//	}
+				//	groupSellerList = append(groupSellerList, sellerItem)
+				//}
+				//groupSellerList = sortDepartmentGroupSellers(groupSellerList)
+				groupItem.ChildrenList = groupSellerList
+				if len(groupSellerList) > 0 {
+					list = append(list, groupItem)
+				}
+			}
+
+			//分组为0的销售(直属部门)
+			departmentSellerList, err := system.GetDepartmentGroupSysUserList(departmentId, 0, roleCodeTypeStr)
+			if err != nil {
+				br.Msg = "获取销售失败"
+				br.ErrMsg = "获取销售失败,Err:" + err.Error()
+				return
+			}
+			if len(departmentSellerList) > 0 {
+				tmpDepartmentGroupSeller := make([]company.DepartmentGroupSellers, 0)
+				for _, seller := range departmentSellerList {
+					sellerItem := company.DepartmentGroupSellers{
+						AdminId: fmt.Sprint(seller.AdminId),
+						//AdminName string `description:"系统用户名称"`
+						RealName:     seller.RealName,
+						Authority:    seller.Authority,
+						RoleTypeCode: seller.RoleTypeCode,
+					}
+					tmpDepartmentGroupSeller = append(tmpDepartmentGroupSeller, sellerItem)
+				}
+				tmpDepartmentGroupSeller = sortDepartmentGroupSellers(tmpDepartmentGroupSeller)
+				list = append(list, tmpDepartmentGroupSeller...)
+			}
+		} else if sysUser.RoleTypeCode == utils.ROLE_TYPE_CODE_RAI_GROUP || (sysUser.Authority == 2 && sysUser.RoleTypeCode != utils.ROLE_TYPE_CODE_FICC_GROUP) {
+			// 权益组长看小组
+			if status == 0 {
+				sellerList, err := system.GetTeamSysUserList(sysUser.GroupId, enabled)
+				if err != nil {
+					br.Msg = "获取销售失败"
+					br.ErrMsg = "获取销售失败,Err:" + err.Error()
+					return
+				}
+				for _, seller := range sellerList {
+					sellerItem := company.DepartmentGroupSellers{
+						AdminId: fmt.Sprint(seller.AdminId),
+						//AdminName string `description:"系统用户名称"`
+						RealName:     seller.RealName,
+						Authority:    seller.Authority,
+						RoleTypeCode: seller.RoleTypeCode,
+					}
+					//fmt.Println("seller.RealName:", seller.RealName)
+					list = append(list, sellerItem)
+				}
+				list = sortDepartmentGroupSellers(list)
+
+				//正常来说没有所有人都应该在某个小组下,要不就是小组名为 无 的小组
+				//分组为0的销售(直属分组)
+				//groupTeamSellerList, err := system.GetGrooupsysUserList(sysUser.GroupId, 0, roleCodeTypeStr)
+				//if err != nil {
+				//	br.Msg = "获取销售失败"
+				//	br.ErrMsg = "获取销售失败,Err:" + err.Error()
+				//	return
+				//}
+				//if len(groupTeamSellerList) > 0 {
+				//	tmpDepartmentGroupSellersList := make([]company.DepartmentGroupSellers, 0)
+				//	for _, seller := range groupTeamSellerList {
+				//		sellerItem := company.DepartmentGroupSellers{
+				//			AdminId: fmt.Sprint(seller.AdminId),
+				//			//AdminName string `description:"系统用户名称"`
+				//			RealName:     seller.RealName,
+				//			Authority:    seller.Authority,
+				//			RoleTypeCode: seller.RoleTypeCode,
+				//		}
+				//		tmpDepartmentGroupSellersList = append(tmpDepartmentGroupSellersList, sellerItem)
+				//	}
+				//	//排个序
+				//	tmpDepartmentGroupSellersList = sortDepartmentGroupSellers(tmpDepartmentGroupSellersList)
+				//	list = append(list, tmpDepartmentGroupSellersList...)
+				//}
+				//sellerList, err := system.GetDepartmentGroupSysUserList(sysUser.DepartmentId, sysUser.GroupId, roleCodeTypeStr)
+				//if err != nil {
+				//	br.Msg = "获取销售失败"
+				//	br.ErrMsg = "获取销售失败,Err:" + err.Error()
+				//	return
+				//}
+				//for _, seller := range sellerList {
+				//	sellerItem := company.DepartmentGroupSellers{
+				//		AdminId: fmt.Sprint(seller.AdminId),
+				//		//AdminName string `description:"系统用户名称"`
+				//		RealName:     seller.RealName,
+				//		Authority:    seller.Authority,
+				//		RoleTypeCode: seller.RoleTypeCode,
+				//	}
+				//	list = append(list, sellerItem)
+				//	list = sortDepartmentGroupSellers(list)
+				//}
+			} else {
+				departmentId := 5
+				groupList, err := system.GetSysGroupByDepartmentId(departmentId)
+				if err != nil {
+					br.Msg = "获取失败"
+					br.ErrMsg = "获取分组失败,Err:" + err.Error()
+					return
+				}
+				for _, group := range groupList {
+					groupSellerList := make([]company.DepartmentGroupSellers, 0)
+					groupItem := company.DepartmentGroupSellers{
+						AdminId: fmt.Sprint("group_", group.DepartmentId),
+						//AdminName string `description:"系统用户名称"`
+						RealName:     group.GroupName,
+						ChildrenList: groupSellerList,
+					}
+					teamList, err := system.GetSysTeamByDepartmentId(group.GroupId)
+					if err != nil {
+						br.Msg = "获取小组失败"
+						br.ErrMsg = "获取小组失败,Err:" + err.Error()
+						return
+					}
+					for _, team := range teamList {
+						teamSellerList := make([]company.DepartmentGroupSellers, 0)
+						teamItem := company.DepartmentGroupSellers{
+							AdminId: fmt.Sprint("team_", team.GroupId),
+							//AdminName string `description:"系统用户名称"`
+							RealName:     team.GroupName,
+							ChildrenList: teamSellerList,
+						}
+						sellerList, err := system.GetTeamSysUserList(team.GroupId, enabled)
+						if err != nil {
+							br.Msg = "获取销售失败"
+							br.ErrMsg = "获取销售失败,Err:" + err.Error()
+							return
+						}
+						for _, seller := range sellerList {
+							sellerItem := company.DepartmentGroupSellers{
+								AdminId: fmt.Sprint(seller.AdminId),
+								//AdminName string `description:"系统用户名称"`
+								RealName:     seller.RealName,
+								Authority:    seller.Authority,
+								RoleTypeCode: seller.RoleTypeCode,
+							}
+							//fmt.Println("seller.RealName:", seller.RealName)
+							teamSellerList = append(teamSellerList, sellerItem)
+						}
+						teamSellerList = sortDepartmentGroupSellers(teamSellerList)
+						teamItem.ChildrenList = teamSellerList
+						if len(teamSellerList) > 0 {
+							groupSellerList = append(groupSellerList, teamItem)
+						}
+					}
+					//分组为0的销售(直属分组)
+					groupTeamSellerList, err := system.GetGrooupsysUserList(group.GroupId, roleCodeTypeStr, enabled)
+					if err != nil {
+						br.Msg = "获取销售失败"
+						br.ErrMsg = "获取销售失败,Err:" + err.Error()
+						return
+					}
+					if len(groupTeamSellerList) > 0 {
+						tmpDepartmentGroupSellersList := make([]company.DepartmentGroupSellers, 0)
+						for _, seller := range groupTeamSellerList {
+							sellerItem := company.DepartmentGroupSellers{
+								AdminId: fmt.Sprint(seller.AdminId),
+								//AdminName string `description:"系统用户名称"`
+								RealName:     seller.RealName,
+								Authority:    seller.Authority,
+								RoleTypeCode: seller.RoleTypeCode,
+							}
+							tmpDepartmentGroupSellersList = append(tmpDepartmentGroupSellersList, sellerItem)
+						}
+						//排个序
+						tmpDepartmentGroupSellersList = sortDepartmentGroupSellers(tmpDepartmentGroupSellersList)
+						groupSellerList = append(groupSellerList, tmpDepartmentGroupSellersList...)
+					}
+
+					//groupItem.ChildrenList = groupSellerList
+					//if len(groupSellerList) > 0 {
+					//	list = append(list, item)
+					//}
+
+					//sellerList, err := system.GetGroupSysUserList(group.DepartmentId)
+					//if err != nil {
+					//	br.Msg = "获取销售失败"
+					//	br.ErrMsg = "获取销售失败,Err:" + err.Error()
+					//	return
+					//}
+					//for _, seller := range sellerList {
+					//	sellerItem := company.DepartmentGroupSellers{
+					//		AdminId: fmt.Sprint(seller.AdminId),
+					//		//AdminName string `description:"系统用户名称"`
+					//		RealName:     seller.RealName,
+					//		Authority:    seller.Authority,
+					//		RoleTypeCode: seller.RoleTypeCode,
+					//	}
+					//	groupSellerList = append(groupSellerList, sellerItem)
+					//}
+					//groupSellerList = sortDepartmentGroupSellers(groupSellerList)
+					groupItem.ChildrenList = groupSellerList
+					if len(groupSellerList) > 0 {
+						list = append(list, groupItem)
+					}
+				}
+
+				//分组为0的销售(直属部门)
+				departmentSellerList, err := system.GetDepartmentGroupSysUserList(departmentId, 0, roleCodeTypeStr)
+				if err != nil {
+					br.Msg = "获取销售失败"
+					br.ErrMsg = "获取销售失败,Err:" + err.Error()
+					return
+				}
+				if len(departmentSellerList) > 0 {
+					tmpDepartmentGroupSeller := make([]company.DepartmentGroupSellers, 0)
+					for _, seller := range departmentSellerList {
+						sellerItem := company.DepartmentGroupSellers{
+							AdminId: fmt.Sprint(seller.AdminId),
+							//AdminName string `description:"系统用户名称"`
+							RealName:     seller.RealName,
+							Authority:    seller.Authority,
+							RoleTypeCode: seller.RoleTypeCode,
+						}
+						tmpDepartmentGroupSeller = append(tmpDepartmentGroupSeller, sellerItem)
+					}
+					tmpDepartmentGroupSeller = sortDepartmentGroupSellers(tmpDepartmentGroupSeller)
+					list = append(list, tmpDepartmentGroupSeller...)
+				}
+			}
+
+		} else if sysUser.RoleTypeCode == utils.ROLE_TYPE_CODE_FICC_GROUP || sysUser.Authority == 2 {
+			// 销售主管看小组
+			if status == 0 {
+				pid, err := system.GetParentIdFromGroup(sysUser.GroupId)
+				if err != nil {
+					br.Msg = "获取失败"
+					br.ErrMsg = "查询父级id异常" + err.Error()
+					return
+				}
+				var realGroupId int
+				if pid != nil && *pid > 0 {
+					//主管角色放在了三级分组下
+					realGroupId = *pid
+				} else {
+					//主管角色放在了二级分组下
+					realGroupId = sysUser.GroupId
+				}
+				groupList, err := system.GetSysGroupByGroupId(realGroupId)
+				if err != nil {
+					br.Msg = "获取失败"
+					br.ErrMsg = "获取分组失败,Err:" + err.Error()
+					return
+				}
+				for _, group := range groupList {
+					groupSellerList := make([]company.DepartmentGroupSellers, 0)
+					//groupItem := company.DepartmentGroupSellers{
+					//	AdminId: fmt.Sprint("group_", group.DepartmentId),
+					//	//AdminName string `description:"系统用户名称"`
+					//	RealName:     group.GroupName,
+					//	ChildrenList: groupSellerList,
+					//}
+					teamList, err := system.GetSysTeamByDepartmentId(group.GroupId)
+					if err != nil {
+						br.Msg = "获取小组失败"
+						br.ErrMsg = "获取小组失败,Err:" + err.Error()
+						return
+					}
+					for _, team := range teamList {
+						teamSellerList := make([]company.DepartmentGroupSellers, 0)
+						teamItem := company.DepartmentGroupSellers{
+							AdminId: fmt.Sprint("team_", team.GroupId),
+							//AdminName string `description:"系统用户名称"`
+							RealName:     team.GroupName,
+							ChildrenList: teamSellerList,
+						}
+						sellerList, err := system.GetTeamSysUserList(team.GroupId, enabled)
+						if err != nil {
+							br.Msg = "获取销售失败"
+							br.ErrMsg = "获取销售失败,Err:" + err.Error()
+							return
+						}
+						for _, seller := range sellerList {
+							sellerItem := company.DepartmentGroupSellers{
+								AdminId: fmt.Sprint(seller.AdminId),
+								//AdminName string `description:"系统用户名称"`
+								RealName:     seller.RealName,
+								Authority:    seller.Authority,
+								RoleTypeCode: seller.RoleTypeCode,
+							}
+							//fmt.Println("seller.RealName:", seller.RealName)
+							teamSellerList = append(teamSellerList, sellerItem)
+						}
+						teamSellerList = sortDepartmentGroupSellers(teamSellerList)
+						teamItem.ChildrenList = teamSellerList
+						if len(teamSellerList) > 0 {
+							groupSellerList = append(groupSellerList, teamItem)
+						}
+					}
+					//分组为0的销售(直属分组)
+					groupTeamSellerList, err := system.GetGrooupsysUserList(realGroupId, roleCodeTypeStr, enabled)
+					if err != nil {
+						br.Msg = "获取销售失败"
+						br.ErrMsg = "获取销售失败,Err:" + err.Error()
+						return
+					}
+					if len(groupTeamSellerList) > 0 {
+						tmpDepartmentGroupSellersList := make([]company.DepartmentGroupSellers, 0)
+						for _, seller := range groupTeamSellerList {
+							sellerItem := company.DepartmentGroupSellers{
+								AdminId: fmt.Sprint(seller.AdminId),
+								//AdminName string `description:"系统用户名称"`
+								RealName:     seller.RealName,
+								Authority:    seller.Authority,
+								RoleTypeCode: seller.RoleTypeCode,
+							}
+							tmpDepartmentGroupSellersList = append(tmpDepartmentGroupSellersList, sellerItem)
+						}
+						//排个序
+						tmpDepartmentGroupSellersList = sortDepartmentGroupSellers(tmpDepartmentGroupSellersList)
+						groupSellerList = append(groupSellerList, tmpDepartmentGroupSellersList...)
+					}
+					list = groupSellerList
+					//groupItem.ChildrenList = groupSellerList
+					//if len(groupSellerList) > 0 {
+					//	list = append(list, item)
+					//}
+
+					//sellerList, err := system.GetGroupSysUserList(group.DepartmentId)
+					//if err != nil {
+					//	br.Msg = "获取销售失败"
+					//	br.ErrMsg = "获取销售失败,Err:" + err.Error()
+					//	return
+					//}
+					//for _, seller := range sellerList {
+					//	sellerItem := company.DepartmentGroupSellers{
+					//		AdminId: fmt.Sprint(seller.AdminId),
+					//		//AdminName string `description:"系统用户名称"`
+					//		RealName:     seller.RealName,
+					//		Authority:    seller.Authority,
+					//		RoleTypeCode: seller.RoleTypeCode,
+					//	}
+					//	groupSellerList = append(groupSellerList, sellerItem)
+					//}
+					//groupSellerList = sortDepartmentGroupSellers(groupSellerList)
+
+					//groupItem.ChildrenList = groupSellerList
+					//if len(groupSellerList) > 0 {
+					//	list = append(list, groupItem)
+					//}
+
+				}
+
+				//分组为0的销售(直属二级分组)
+				//departmentSellerList, err := system.GetDepartmentGroupSysUserList(2, realGroupId, roleCodeTypeStr)
+				//if err != nil {
+				//	br.Msg = "获取销售失败"
+				//	br.ErrMsg = "获取销售失败,Err:" + err.Error()
+				//	return
+				//}
+				//if len(departmentSellerList) > 0 {
+				//	tmpDepartmentGroupSeller := make([]company.DepartmentGroupSellers, 0)
+				//	for _, seller := range departmentSellerList {
+				//		sellerItem := company.DepartmentGroupSellers{
+				//			AdminId: fmt.Sprint(seller.AdminId),
+				//			//AdminName string `description:"系统用户名称"`
+				//			RealName:     seller.RealName,
+				//			Authority:    seller.Authority,
+				//			RoleTypeCode: seller.RoleTypeCode,
+				//		}
+				//		tmpDepartmentGroupSeller = append(tmpDepartmentGroupSeller, sellerItem)
+				//	}
+				//	tmpDepartmentGroupSeller = sortDepartmentGroupSellers(tmpDepartmentGroupSeller)
+				//	list = append(list, tmpDepartmentGroupSeller...)
+				//}
+				//pid, err := company.GetParentIdFromGroup(sysUser.GroupId)
+				//if err != nil {
+				//	br.Msg = "获取失败"
+				//	br.ErrMsg = "查询父级id异常" + err.Error()
+				//	return
+				//}
+				//if pid != nil && *pid > 0 {
+				//	//主管角色放在了三级分组下
+				//	//
+				//
+				//
+				//} else {
+				//	//主管角色放在了二级分组下
+				//
+				//}
+				//ids, err := company.GetGroupIdsByParentId(*pid)
+				//if err != nil {
+				//	br.Msg = "获取失败"
+				//	br.ErrMsg = "查询父级id异常" + err.Error()
+				//	return
+				//}
+				//var idSlice []string
+				//var sid string
+				//for _, id := range ids {
+				//	idSlice = append(idSlice, *id)
+				//}
+				//sid = strings.Join(idSlice, ",")
+				//sellerList, err := system.GetTeamSysUserListByIds(sid)
+				//if err != nil {
+				//	br.Msg = "获取销售失败"
+				//	br.ErrMsg = "获取销售失败,Err:" + err.Error()
+				//	return
+				//}
+				//for _, seller := range sellerList {
+				//	sellerItem := company.DepartmentGroupSellers{
+				//		AdminId: fmt.Sprint(seller.AdminId),
+				//		//AdminName string `description:"系统用户名称"`
+				//		RealName:     seller.RealName,
+				//		Authority:    seller.Authority,
+				//		RoleTypeCode: seller.RoleTypeCode,
+				//	}
+				//	fmt.Println("seller.RealName:", seller.RealName)
+				//	list = append(list, sellerItem)
+				//}
+				//list = sortDepartmentGroupSellers(list)
+				//
+				////分组为0的销售(直属分组)
+				//groupTeamSellerList, err := system.GetGrooupsysUserList(sysUser.GroupId, roleCodeTypeStr)
+				//if err != nil {
+				//	br.Msg = "获取销售失败"
+				//	br.ErrMsg = "获取销售失败,Err:" + err.Error()
+				//	return
+				//}
+				//if len(groupTeamSellerList) > 0 {
+				//	tmpDepartmentGroupSellersList := make([]company.DepartmentGroupSellers, 0)
+				//	for _, seller := range groupTeamSellerList {
+				//		sellerItem := company.DepartmentGroupSellers{
+				//			AdminId: fmt.Sprint(seller.AdminId),
+				//			//AdminName string `description:"系统用户名称"`
+				//			RealName:     seller.RealName,
+				//			Authority:    seller.Authority,
+				//			RoleTypeCode: seller.RoleTypeCode,
+				//		}
+				//		tmpDepartmentGroupSellersList = append(tmpDepartmentGroupSellersList, sellerItem)
+				//	}
+				//	//排个序
+				//	tmpDepartmentGroupSellersList = sortDepartmentGroupSellers(tmpDepartmentGroupSellersList)
+				//	list = append(list, tmpDepartmentGroupSellersList...)
+				//}
+				//sellerList, err := system.GetDepartmentGroupSysUserList(sysUser.DepartmentId, sysUser.GroupId, roleCodeTypeStr)
+				//if err != nil {
+				//	br.Msg = "获取销售失败"
+				//	br.ErrMsg = "获取销售失败,Err:" + err.Error()
+				//	return
+				//}
+				//for _, seller := range sellerList {
+				//	sellerItem := company.DepartmentGroupSellers{
+				//		AdminId: fmt.Sprint(seller.AdminId),
+				//		//AdminName string `description:"系统用户名称"`
+				//		RealName:     seller.RealName,
+				//		Authority:    seller.Authority,
+				//		RoleTypeCode: seller.RoleTypeCode,
+				//	}
+				//	list = append(list, sellerItem)
+				//	list = sortDepartmentGroupSellers(list)
+				//}
+
+				//正常来说没有所有人都应该在某个小组下,要不就是小组名为 无 的小组
+			} else {
+				//var departmentStr string
+				//if utils.RunMode == "release" {
+				//	departmentStr = `1,2`
+				//} else {
+				//	departmentStr = `1,2`
+				//}
+				departmentStr := `1,2`
+				departmentList, err := system.GetDepartmentListByIds(departmentStr)
+				if err != nil {
+					br.Msg = "获取失败"
+					br.ErrMsg = "获取部门失败,Err:" + err.Error()
+					return
+				}
+				for _, department := range departmentList {
+					departmentGroupSellerList := make([]company.DepartmentGroupSellers, 0)
+					item := company.DepartmentGroupSellers{
+						AdminId: fmt.Sprint("department_", department.DepartmentId),
+						//AdminName string `description:"系统用户名称"`
+						RealName:     department.DepartmentName,
+						ChildrenList: departmentGroupSellerList,
+					}
+
+					groupList, err := system.GetSysGroupByDepartmentId(department.DepartmentId)
+					if err != nil {
+						br.Msg = "获取失败"
+						br.ErrMsg = "获取分组失败,Err:" + err.Error()
+						return
+					}
+					for _, group := range groupList {
+						groupSellerList := make([]company.DepartmentGroupSellers, 0)
+						groupItem := company.DepartmentGroupSellers{
+							AdminId: fmt.Sprint("group_", group.DepartmentId),
+							//AdminName string `description:"系统用户名称"`
+							RealName:     group.GroupName,
+							ChildrenList: groupSellerList,
+						}
+						teamList, err := system.GetSysTeamByDepartmentId(group.GroupId)
+						if err != nil {
+							br.Msg = "获取小组失败"
+							br.ErrMsg = "获取小组失败,Err:" + err.Error()
+							return
+						}
+						for _, team := range teamList {
+							teamSellerList := make([]company.DepartmentGroupSellers, 0)
+							teamItem := company.DepartmentGroupSellers{
+								AdminId: fmt.Sprint("team_", team.GroupId),
+								//AdminName string `description:"系统用户名称"`
+								RealName:     team.GroupName,
+								ChildrenList: teamSellerList,
+							}
+							sellerList, err := system.GetTeamSysUserList(team.GroupId, enabled)
+							if err != nil {
+								br.Msg = "获取销售失败"
+								br.ErrMsg = "获取销售失败,Err:" + err.Error()
+								return
+							}
+							for _, seller := range sellerList {
+								sellerItem := company.DepartmentGroupSellers{
+									AdminId: fmt.Sprint(seller.AdminId),
+									//AdminName string `description:"系统用户名称"`
+									RealName:     seller.RealName,
+									Authority:    seller.Authority,
+									RoleTypeCode: seller.RoleTypeCode,
+								}
+								//fmt.Println("seller.RealName:", seller.RealName)
+								teamSellerList = append(teamSellerList, sellerItem)
+							}
+							teamSellerList = sortDepartmentGroupSellers(teamSellerList)
+							teamItem.ChildrenList = teamSellerList
+							if len(teamSellerList) > 0 {
+								groupSellerList = append(groupSellerList, teamItem)
+							}
+						}
+						//分组为0的人,不光是销售(直属分组)
+						groupTeamSellerList, err := system.GetGroupSysUserList(group.GroupId)
+						if err != nil {
+							br.Msg = "获取销售失败"
+							br.ErrMsg = "获取销售失败,Err:" + err.Error()
+							return
+						}
+						if len(groupTeamSellerList) > 0 {
+							tmpDepartmentGroupSellersList := make([]company.DepartmentGroupSellers, 0)
+							for _, seller := range groupTeamSellerList {
+								sellerItem := company.DepartmentGroupSellers{
+									AdminId: fmt.Sprint(seller.AdminId),
+									//AdminName string `description:"系统用户名称"`
+									RealName:     seller.RealName,
+									Authority:    seller.Authority,
+									RoleTypeCode: seller.RoleTypeCode,
+								}
+								tmpDepartmentGroupSellersList = append(tmpDepartmentGroupSellersList, sellerItem)
+							}
+							//排个序
+							tmpDepartmentGroupSellersList = sortDepartmentGroupSellers(tmpDepartmentGroupSellersList)
+							groupSellerList = append(groupSellerList, tmpDepartmentGroupSellersList...)
+						}
+
+						//groupItem.ChildrenList = groupSellerList
+						//if len(groupSellerList) > 0 {
+						//	list = append(list, item)
+						//}
+
+						//sellerList, err := system.GetGroupSysUserList(group.DepartmentId)
+						//if err != nil {
+						//	br.Msg = "获取销售失败"
+						//	br.ErrMsg = "获取销售失败,Err:" + err.Error()
+						//	return
+						//}
+						//for _, seller := range sellerList {
+						//	sellerItem := company.DepartmentGroupSellers{
+						//		AdminId: fmt.Sprint(seller.AdminId),
+						//		//AdminName string `description:"系统用户名称"`
+						//		RealName:     seller.RealName,
+						//		Authority:    seller.Authority,
+						//		RoleTypeCode: seller.RoleTypeCode,
+						//	}
+						//	groupSellerList = append(groupSellerList, sellerItem)
+						//}
+						//groupSellerList = sortDepartmentGroupSellers(groupSellerList)
+						groupItem.ChildrenList = groupSellerList
+						if len(groupSellerList) > 0 {
+							departmentGroupSellerList = append(departmentGroupSellerList, groupItem)
+						}
+					}
+
+					//分组为0的人,不光是销售(直属部门)
+					departmentSellerList, err := system.GetDepartmentGroupSysUserList(department.DepartmentId, 0, "")
+					if err != nil {
+						br.Msg = "获取销售失败"
+						br.ErrMsg = "获取销售失败,Err:" + err.Error()
+						return
+					}
+					if len(departmentSellerList) > 0 {
+						tmpDepartmentGroupSellersList := make([]company.DepartmentGroupSellers, 0)
+						for _, seller := range departmentSellerList {
+							sellerItem := company.DepartmentGroupSellers{
+								AdminId: fmt.Sprint(seller.AdminId),
+								//AdminName string `description:"系统用户名称"`
+								RealName:     seller.RealName,
+								Authority:    seller.Authority,
+								RoleTypeCode: seller.RoleTypeCode,
+							}
+							tmpDepartmentGroupSellersList = append(tmpDepartmentGroupSellersList, sellerItem)
+						}
+						//排个序
+						tmpDepartmentGroupSellersList = sortDepartmentGroupSellers(tmpDepartmentGroupSellersList)
+						departmentGroupSellerList = append(departmentGroupSellerList, tmpDepartmentGroupSellersList...)
+					}
+
+					item.ChildrenList = departmentGroupSellerList
+					if len(departmentGroupSellerList) > 0 {
+						list = append(list, item)
+					}
+				}
+			}
+		} else if sysUser.RoleTypeCode == utils.ROLE_TYPE_CODE_FICC_SELLER && status == 1 {
+			//var departmentStr string
+			//if utils.RunMode == "release" {
+			//	departmentStr = `1,2`
+			//} else {
+			//	departmentStr = `1,2`
+			//}
+			departmentStr := `1,2`
+			departmentList, err := system.GetDepartmentListByIds(departmentStr)
+			if err != nil {
+				br.Msg = "获取失败"
+				br.ErrMsg = "获取部门失败,Err:" + err.Error()
+				return
+			}
+			for _, department := range departmentList {
+				departmentGroupSellerList := make([]company.DepartmentGroupSellers, 0)
+				item := company.DepartmentGroupSellers{
+					AdminId: fmt.Sprint("department_", department.DepartmentId),
+					//AdminName string `description:"系统用户名称"`
+					RealName:     department.DepartmentName,
+					ChildrenList: departmentGroupSellerList,
+				}
+
+				groupList, err := system.GetSysGroupByDepartmentId(department.DepartmentId)
+				if err != nil {
+					br.Msg = "获取失败"
+					br.ErrMsg = "获取分组失败,Err:" + err.Error()
+					return
+				}
+				for _, group := range groupList {
+					groupSellerList := make([]company.DepartmentGroupSellers, 0)
+					groupItem := company.DepartmentGroupSellers{
+						AdminId: fmt.Sprint("group_", group.DepartmentId),
+						//AdminName string `description:"系统用户名称"`
+						RealName:     group.GroupName,
+						ChildrenList: groupSellerList,
+					}
+					teamList, err := system.GetSysTeamByDepartmentId(group.GroupId)
+					if err != nil {
+						br.Msg = "获取小组失败"
+						br.ErrMsg = "获取小组失败,Err:" + err.Error()
+						return
+					}
+					for _, team := range teamList {
+						teamSellerList := make([]company.DepartmentGroupSellers, 0)
+						teamItem := company.DepartmentGroupSellers{
+							AdminId: fmt.Sprint("team_", team.GroupId),
+							//AdminName string `description:"系统用户名称"`
+							RealName:     team.GroupName,
+							ChildrenList: teamSellerList,
+						}
+						sellerList, err := system.GetTeamSysUserList(team.GroupId, enabled)
+						if err != nil {
+							br.Msg = "获取销售失败"
+							br.ErrMsg = "获取销售失败,Err:" + err.Error()
+							return
+						}
+						for _, seller := range sellerList {
+							sellerItem := company.DepartmentGroupSellers{
+								AdminId: fmt.Sprint(seller.AdminId),
+								//AdminName string `description:"系统用户名称"`
+								RealName:     seller.RealName,
+								Authority:    seller.Authority,
+								RoleTypeCode: seller.RoleTypeCode,
+							}
+							//fmt.Println("seller.RealName:", seller.RealName)
+							teamSellerList = append(teamSellerList, sellerItem)
+						}
+						teamSellerList = sortDepartmentGroupSellers(teamSellerList)
+						teamItem.ChildrenList = teamSellerList
+						if len(teamSellerList) > 0 {
+							groupSellerList = append(groupSellerList, teamItem)
+						}
+					}
+					//分组为0的人,不光是销售(直属分组)
+					groupTeamSellerList, err := system.GetGroupSysUserList(group.GroupId)
+					if err != nil {
+						br.Msg = "获取销售失败"
+						br.ErrMsg = "获取销售失败,Err:" + err.Error()
+						return
+					}
+					if len(groupTeamSellerList) > 0 {
+						tmpDepartmentGroupSellersList := make([]company.DepartmentGroupSellers, 0)
+						for _, seller := range groupTeamSellerList {
+							sellerItem := company.DepartmentGroupSellers{
+								AdminId: fmt.Sprint(seller.AdminId),
+								//AdminName string `description:"系统用户名称"`
+								RealName:     seller.RealName,
+								Authority:    seller.Authority,
+								RoleTypeCode: seller.RoleTypeCode,
+							}
+							tmpDepartmentGroupSellersList = append(tmpDepartmentGroupSellersList, sellerItem)
+						}
+						//排个序
+						tmpDepartmentGroupSellersList = sortDepartmentGroupSellers(tmpDepartmentGroupSellersList)
+						groupSellerList = append(groupSellerList, tmpDepartmentGroupSellersList...)
+					}
+
+					//groupItem.ChildrenList = groupSellerList
+					//if len(groupSellerList) > 0 {
+					//	list = append(list, item)
+					//}
+
+					//sellerList, err := system.GetGroupSysUserList(group.DepartmentId)
+					//if err != nil {
+					//	br.Msg = "获取销售失败"
+					//	br.ErrMsg = "获取销售失败,Err:" + err.Error()
+					//	return
+					//}
+					//for _, seller := range sellerList {
+					//	sellerItem := company.DepartmentGroupSellers{
+					//		AdminId: fmt.Sprint(seller.AdminId),
+					//		//AdminName string `description:"系统用户名称"`
+					//		RealName:     seller.RealName,
+					//		Authority:    seller.Authority,
+					//		RoleTypeCode: seller.RoleTypeCode,
+					//	}
+					//	groupSellerList = append(groupSellerList, sellerItem)
+					//}
+					//groupSellerList = sortDepartmentGroupSellers(groupSellerList)
+					groupItem.ChildrenList = groupSellerList
+					if len(groupSellerList) > 0 {
+						departmentGroupSellerList = append(departmentGroupSellerList, groupItem)
+					}
+				}
+
+				//分组为0的人,不光是销售(直属部门)
+				departmentSellerList, err := system.GetDepartmentGroupSysUserList(department.DepartmentId, 0, "")
+				if err != nil {
+					br.Msg = "获取销售失败"
+					br.ErrMsg = "获取销售失败,Err:" + err.Error()
+					return
+				}
+				if len(departmentSellerList) > 0 {
+					tmpDepartmentGroupSellersList := make([]company.DepartmentGroupSellers, 0)
+					for _, seller := range departmentSellerList {
+						sellerItem := company.DepartmentGroupSellers{
+							AdminId: fmt.Sprint(seller.AdminId),
+							//AdminName string `description:"系统用户名称"`
+							RealName:     seller.RealName,
+							Authority:    seller.Authority,
+							RoleTypeCode: seller.RoleTypeCode,
+						}
+						tmpDepartmentGroupSellersList = append(tmpDepartmentGroupSellersList, sellerItem)
+					}
+					//排个序
+					tmpDepartmentGroupSellersList = sortDepartmentGroupSellers(tmpDepartmentGroupSellersList)
+					departmentGroupSellerList = append(departmentGroupSellerList, tmpDepartmentGroupSellersList...)
+				}
+
+				item.ChildrenList = departmentGroupSellerList
+				if len(departmentGroupSellerList) > 0 {
+					list = append(list, item)
+				}
+			}
+		} else if sysUser.RoleTypeCode == utils.ROLE_TYPE_CODE_RAI_SELLER && status == 1 {
+			// 权益销售可以看到权益销售部的所有人
+			departmentId := 5
+			groupList, err := system.GetSysGroupByDepartmentId(departmentId)
+			if err != nil {
+				br.Msg = "获取失败"
+				br.ErrMsg = "获取分组失败,Err:" + err.Error()
+				return
+			}
+			for _, group := range groupList {
+				groupSellerList := make([]company.DepartmentGroupSellers, 0)
+				groupItem := company.DepartmentGroupSellers{
+					AdminId: fmt.Sprint("group_", group.DepartmentId),
+					//AdminName string `description:"系统用户名称"`
+					RealName:     group.GroupName,
+					ChildrenList: groupSellerList,
+				}
+				teamList, err := system.GetSysTeamByDepartmentId(group.GroupId)
+				if err != nil {
+					br.Msg = "获取小组失败"
+					br.ErrMsg = "获取小组失败,Err:" + err.Error()
+					return
+				}
+				for _, team := range teamList {
+					teamSellerList := make([]company.DepartmentGroupSellers, 0)
+					teamItem := company.DepartmentGroupSellers{
+						AdminId: fmt.Sprint("team_", team.GroupId),
+						//AdminName string `description:"系统用户名称"`
+						RealName:     team.GroupName,
+						ChildrenList: teamSellerList,
+					}
+					sellerList, err := system.GetTeamSysUserList(team.GroupId, enabled)
+					if err != nil {
+						br.Msg = "获取销售失败"
+						br.ErrMsg = "获取销售失败,Err:" + err.Error()
+						return
+					}
+					for _, seller := range sellerList {
+						sellerItem := company.DepartmentGroupSellers{
+							AdminId: fmt.Sprint(seller.AdminId),
+							//AdminName string `description:"系统用户名称"`
+							RealName:     seller.RealName,
+							Authority:    seller.Authority,
+							RoleTypeCode: seller.RoleTypeCode,
+						}
+						//fmt.Println("seller.RealName:", seller.RealName)
+						teamSellerList = append(teamSellerList, sellerItem)
+					}
+					teamSellerList = sortDepartmentGroupSellers(teamSellerList)
+					teamItem.ChildrenList = teamSellerList
+					if len(teamSellerList) > 0 {
+						groupSellerList = append(groupSellerList, teamItem)
+					}
+				}
+				//分组为0的销售(直属分组)
+				groupTeamSellerList, err := system.GetGrooupsysUserList(group.GroupId, roleCodeTypeStr, enabled)
+				if err != nil {
+					br.Msg = "获取销售失败"
+					br.ErrMsg = "获取销售失败,Err:" + err.Error()
+					return
+				}
+				if len(groupTeamSellerList) > 0 {
+					tmpDepartmentGroupSellersList := make([]company.DepartmentGroupSellers, 0)
+					for _, seller := range groupTeamSellerList {
+						sellerItem := company.DepartmentGroupSellers{
+							AdminId: fmt.Sprint(seller.AdminId),
+							//AdminName string `description:"系统用户名称"`
+							RealName:     seller.RealName,
+							Authority:    seller.Authority,
+							RoleTypeCode: seller.RoleTypeCode,
+						}
+						tmpDepartmentGroupSellersList = append(tmpDepartmentGroupSellersList, sellerItem)
+					}
+					//排个序
+					tmpDepartmentGroupSellersList = sortDepartmentGroupSellers(tmpDepartmentGroupSellersList)
+					groupSellerList = append(groupSellerList, tmpDepartmentGroupSellersList...)
+				}
+
+				//groupItem.ChildrenList = groupSellerList
+				//if len(groupSellerList) > 0 {
+				//	list = append(list, item)
+				//}
+
+				//sellerList, err := system.GetGroupSysUserList(group.DepartmentId)
+				//if err != nil {
+				//	br.Msg = "获取销售失败"
+				//	br.ErrMsg = "获取销售失败,Err:" + err.Error()
+				//	return
+				//}
+				//for _, seller := range sellerList {
+				//	sellerItem := company.DepartmentGroupSellers{
+				//		AdminId: fmt.Sprint(seller.AdminId),
+				//		//AdminName string `description:"系统用户名称"`
+				//		RealName:     seller.RealName,
+				//		Authority:    seller.Authority,
+				//		RoleTypeCode: seller.RoleTypeCode,
+				//	}
+				//	groupSellerList = append(groupSellerList, sellerItem)
+				//}
+				//groupSellerList = sortDepartmentGroupSellers(groupSellerList)
+				groupItem.ChildrenList = groupSellerList
+				if len(groupSellerList) > 0 {
+					list = append(list, groupItem)
+				}
+			}
+
+			//分组为0的销售(直属部门)
+			departmentSellerList, err := system.GetDepartmentGroupSysUserList(departmentId, 0, roleCodeTypeStr)
+			if err != nil {
+				br.Msg = "获取销售失败"
+				br.ErrMsg = "获取销售失败,Err:" + err.Error()
+				return
+			}
+			if len(departmentSellerList) > 0 {
+				tmpDepartmentGroupSeller := make([]company.DepartmentGroupSellers, 0)
+				for _, seller := range departmentSellerList {
+					sellerItem := company.DepartmentGroupSellers{
+						AdminId: fmt.Sprint(seller.AdminId),
+						//AdminName string `description:"系统用户名称"`
+						RealName:     seller.RealName,
+						Authority:    seller.Authority,
+						RoleTypeCode: seller.RoleTypeCode,
+					}
+					tmpDepartmentGroupSeller = append(tmpDepartmentGroupSeller, sellerItem)
+				}
+				tmpDepartmentGroupSeller = sortDepartmentGroupSellers(tmpDepartmentGroupSeller)
+				list = append(list, tmpDepartmentGroupSeller...)
+			}
+		} else {
+			//看自己
+			sellerItem := company.DepartmentGroupSellers{
+				AdminId: fmt.Sprint(sysUser.AdminId),
+				//AdminName string `description:"系统用户名称"`
+				RealName: sysUser.RealName,
+			}
+			list = append(list, sellerItem)
+		}
+	} else {
+		departmentId := sysUser.DepartmentId
+		switch productId {
+		case 1:
+			departmentId = 2
+		case 5:
+			departmentId = 5
+		}
+		//查询整个部门的小组、销售
+		groupList, err := system.GetSysGroupByDepartmentId(departmentId)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取分组失败,Err:" + err.Error()
+			return
+		}
+		for _, group := range groupList {
+			groupSellerList := make([]company.DepartmentGroupSellers, 0)
+			groupItem := company.DepartmentGroupSellers{
+				AdminId: fmt.Sprint("group_", group.DepartmentId),
+				//AdminName string `description:"系统用户名称"`
+				RealName:     group.GroupName,
+				ChildrenList: groupSellerList,
+			}
+			teamList, err := system.GetSysTeamByDepartmentId(group.GroupId)
+			if err != nil {
+				br.Msg = "获取小组失败"
+				br.ErrMsg = "获取小组失败,Err:" + err.Error()
+				return
+			}
+			for _, team := range teamList {
+				teamSellerList := make([]company.DepartmentGroupSellers, 0)
+				teamItem := company.DepartmentGroupSellers{
+					AdminId: fmt.Sprint("team_", team.GroupId),
+					//AdminName string `description:"系统用户名称"`
+					RealName:     team.GroupName,
+					ChildrenList: teamSellerList,
+				}
+				sellerList, err := system.GetTeamSysUserList(team.GroupId, enabled)
+				if err != nil {
+					br.Msg = "获取销售失败"
+					br.ErrMsg = "获取销售失败,Err:" + err.Error()
+					return
+				}
+				for _, seller := range sellerList {
+					sellerItem := company.DepartmentGroupSellers{
+						AdminId: fmt.Sprint(seller.AdminId),
+						//AdminName string `description:"系统用户名称"`
+						RealName:     seller.RealName,
+						Authority:    seller.Authority,
+						RoleTypeCode: seller.RoleTypeCode,
+					}
+					//fmt.Println("seller.RealName:", seller.RealName)
+					teamSellerList = append(teamSellerList, sellerItem)
+				}
+				teamSellerList = sortDepartmentGroupSellers(teamSellerList)
+				teamItem.ChildrenList = teamSellerList
+				if len(teamSellerList) > 0 {
+					groupSellerList = append(groupSellerList, teamItem)
+				}
+			}
+			//分组为0的销售(直属分组)
+			groupTeamSellerList, err := system.GetGrooupsysUserList(group.GroupId, roleCodeTypeStr, enabled)
+			if err != nil {
+				br.Msg = "获取销售失败"
+				br.ErrMsg = "获取销售失败,Err:" + err.Error()
+				return
+			}
+			if len(groupTeamSellerList) > 0 {
+				tmpDepartmentGroupSellersList := make([]company.DepartmentGroupSellers, 0)
+				for _, seller := range groupTeamSellerList {
+					sellerItem := company.DepartmentGroupSellers{
+						AdminId: fmt.Sprint(seller.AdminId),
+						//AdminName string `description:"系统用户名称"`
+						RealName:     seller.RealName,
+						Authority:    seller.Authority,
+						RoleTypeCode: seller.RoleTypeCode,
+					}
+					tmpDepartmentGroupSellersList = append(tmpDepartmentGroupSellersList, sellerItem)
+				}
+				//排个序
+				tmpDepartmentGroupSellersList = sortDepartmentGroupSellers(tmpDepartmentGroupSellersList)
+				groupSellerList = append(groupSellerList, tmpDepartmentGroupSellersList...)
+			}
+
+			groupItem.ChildrenList = groupSellerList
+			if len(groupSellerList) > 0 {
+				list = append(list, groupItem)
+			}
+		}
+
+		//分组为0的销售(直属部门)
+		departmentSellerList, err := system.GetDepartmentGroupSysUserList(departmentId, 0, roleCodeTypeStr)
+		if err != nil {
+			br.Msg = "获取销售失败"
+			br.ErrMsg = "获取销售失败,Err:" + err.Error()
+			return
+		}
+		if len(departmentSellerList) > 0 {
+			tmpDepartmentGroupSeller := make([]company.DepartmentGroupSellers, 0)
+			for _, seller := range departmentSellerList {
+				sellerItem := company.DepartmentGroupSellers{
+					AdminId: fmt.Sprint(seller.AdminId),
+					//AdminName string `description:"系统用户名称"`
+					RealName:     seller.RealName,
+					Authority:    seller.Authority,
+					RoleTypeCode: seller.RoleTypeCode,
+				}
+				tmpDepartmentGroupSeller = append(tmpDepartmentGroupSeller, sellerItem)
+			}
+			//排个序
+			tmpDepartmentGroupSeller = sortDepartmentGroupSellers(tmpDepartmentGroupSeller)
+			list = append(list, tmpDepartmentGroupSeller...)
+		}
+	}
+
+	// 海外销售部-目前无权限, 均可见
+	seaDepartment, e := system.GetSysDepartmentByName("海外销售部")
+	if e != nil && e.Error() != utils.ErrNoRow() {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取海外销售部失败, Err: " + e.Error()
+		return
+	}
+	if e == nil {
+		// 获取部门下的销售人员
+		seaSellers, e := system.GetDepartmentGroupSysUserList(seaDepartment.DepartmentId, 0, "")
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取海外销售失败, Err: " + e.Error()
+			return
+		}
+		// 获取分组
+		seaGroups, e := system.GetSysGroupListByDepartmentId(seaDepartment.DepartmentId)
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取海外销售部分组失败, Err: " + e.Error()
+			return
+		}
+
+		var d company.DepartmentGroupSellers
+		d.AdminId = fmt.Sprintf("department_%d", seaDepartment.DepartmentId)
+		d.RealName = seaDepartment.DepartmentName
+		children := make([]company.DepartmentGroupSellers, 0)
+		// 无分组
+		if len(seaGroups) == 0 {
+			for _, s := range seaSellers {
+				children = append(children, company.DepartmentGroupSellers{
+					AdminId:      strconv.Itoa(s.AdminId),
+					RealName:     s.RealName,
+					RoleTypeCode: s.RoleTypeCode,
+					Authority:    s.Authority,
+				})
+			}
+		}
+		// 有分组
+		if len(seaGroups) > 0 {
+			// 销售map
+			sellerMap := make(map[int][]company.DepartmentGroupSellers)
+			for _, s := range seaSellers {
+				sellerMap[s.GroupId] = append(sellerMap[s.GroupId], company.DepartmentGroupSellers{
+					AdminId:      strconv.Itoa(s.AdminId),
+					RealName:     s.RealName,
+					RoleTypeCode: s.RoleTypeCode,
+					Authority:    s.Authority,
+				})
+			}
+			// 小组map
+			childrenMap := make(map[int][]company.DepartmentGroupSellers)
+			for _, g := range seaGroups {
+				if g.ParentId == 0 {
+					continue
+				}
+				childrenMap[g.ParentId] = append(childrenMap[g.ParentId], company.DepartmentGroupSellers{
+					AdminId:      fmt.Sprintf("team_%d", g.GroupId),
+					RealName:     g.GroupName,
+					ChildrenList: sellerMap[g.GroupId],
+				})
+			}
+			// 大组
+			for _, g := range seaGroups {
+				if g.ParentId > 0 {
+					continue
+				}
+				c := childrenMap[g.GroupId]
+				if len(sellerMap[g.GroupId]) > 0 {
+					c = append(c, sellerMap[g.GroupId]...)
+				}
+				children = append(children, company.DepartmentGroupSellers{
+					AdminId:      fmt.Sprintf("group_%d", g.GroupId),
+					RealName:     g.GroupName,
+					ChildrenList: c,
+				})
+			}
+		}
+		d.ChildrenList = children
+		list = append(list, d)
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = company.DepartmentGroupSellersResp{
+		List: list,
+	}
+}
+
+// sortDepartmentGroupSellers 部门用户排序
+func sortDepartmentGroupSellers(adminList []company.DepartmentGroupSellers) (list []company.DepartmentGroupSellers) {
+	sellerMap := make(map[int][]company.DepartmentGroupSellers)
+	for _, admin := range adminList {
+		index := 0 //0:超管,1:部门管理员,2:组长,3:普通人
+		switch admin.RoleTypeCode {
+		//case utils.ROLE_TYPE_CODE_ADMIN:
+		//	index = 0
+		//case utils.ROLE_TYPE_CODE_FICC_ADMIN, utils.ROLE_TYPE_CODE_RAI_ADMIN:
+		//	index = 1
+		//case utils.ROLE_TYPE_CODE_FICC_GROUP, utils.ROLE_TYPE_CODE_RAI_GROUP:
+		//	index = 2
+		default:
+			//0:无,1:部门负责人,2:小组负责人,3:超级管理员
+			switch admin.Authority {
+			//case 1:
+			//	index = 1
+			case 2:
+				index = 2
+			//case 3:
+			//	index = 0
+			default:
+				index = 3
+			}
+		}
+		sellerList, ok := sellerMap[index]
+		if !ok {
+			sellerList = make([]company.DepartmentGroupSellers, 0)
+		}
+		sellerList = append(sellerList, admin)
+		sellerMap[index] = sellerList
+	}
+
+	var indexList = []int{0, 1, 2, 3}
+	for _, index := range indexList {
+		sellerList, ok := sellerMap[index]
+		if ok {
+			list = append(list, sellerList...)
+		}
+	}
+	return
+}

+ 135 - 0
controllers/crm_config.go

@@ -0,0 +1,135 @@
+package controllers
+
+import (
+	"encoding/json"
+	"eta/eta_forum_admin/models"
+	"eta/eta_forum_admin/models/system"
+	"strings"
+)
+
+// CrmConfigController 基础配置
+type CrmConfigController struct {
+	BaseAuthController
+}
+
+// Save
+// @Title 保存配置
+// @Description 保存配置
+// @Param	request	body map[string]interface{} true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /save [post]
+func (this *CrmConfigController) Save() {
+	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 map[string]interface{}
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+
+	// 获取配置信息
+	list, e := system.GetCrmConfig()
+	if e != nil {
+		br.Msg = "保存失败"
+		br.ErrMsg = "获取配置列表失败, Err: " + e.Error()
+		return
+	}
+	confMap := make(map[string]*system.CrmConfig)
+	for _, v := range list {
+		if v.IsShow == 1 {
+			confMap[v.ConfigCode] = v
+		}
+	}
+
+	// 根据配置类型取值
+	updates := make([]system.ConfUpdateItem, 0)
+	for k, v := range req {
+		// 过滤掉表中没有的key
+		conf := confMap[k]
+		if conf == nil {
+			continue
+		}
+		str, ok := v.(string)
+		if !ok {
+			continue
+		}
+		str = strings.TrimSpace(str)
+		if conf.Necessary == 1 && str == "" {
+			br.Msg = conf.Remark + "不可为空"
+			return
+		}
+		updates = append(updates, system.ConfUpdateItem{
+			ConfigCode:  k,
+			ConfigValue: str,
+		})
+
+	}
+
+	if len(updates) > 0 {
+		if e = system.UpdateCrmConfigMulti(updates); e != nil {
+			br.Msg = "保存失败"
+			br.ErrMsg = "保存商家配置失败, Err: " + e.Error()
+			return
+		}
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// Fetch
+// @Title 获取配置
+// @Description 获取配置
+// @Success 200 Ret=200 获取成功
+// @router /fetch [get]
+func (this *CrmConfigController) Fetch() {
+	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
+	}
+
+	// 获取配置信息
+	tmpList, e := system.GetCrmConfig()
+	if e != nil {
+		br.Msg = "保存失败"
+		br.ErrMsg = "获取配置列表失败, Err: " + e.Error()
+		return
+	}
+	list := make(map[string]string)
+	for _, v := range tmpList {
+		if v.IsShow == 1 {
+			list[v.ConfigCode] = v.ConfigValue
+		}
+	}
+
+	br.Data = list
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}

+ 1861 - 0
controllers/eta_business/eta_business.go

@@ -0,0 +1,1861 @@
+package eta_business
+
+import (
+	"encoding/json"
+	"eta/eta_forum_admin/controllers"
+	"eta/eta_forum_admin/models"
+	"eta/eta_forum_admin/models/eta_business"
+	"eta/eta_forum_admin/models/help_doc"
+	"eta/eta_forum_admin/services"
+	etaBusinessService "eta/eta_forum_admin/services/eta_business"
+	"eta/eta_forum_admin/utils"
+	"fmt"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"sort"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// EtaBusinessController ETA商家
+type EtaBusinessController struct {
+	controllers.BaseAuthController
+}
+
+// PageList
+// @Title 商家列表-分页
+// @Description 商家列表-分页
+// @Param   Keyword			query	string	false	"关键词: 商家名称/社会信用码/商家编码"
+// @Param   SellerIds		query	string	false	"销售IDs"
+// @Param   SigningStatus	query	string	false	"签约状态: 1-首次签约; 2-续约中; 3-已终止"
+// @Param   Province		query	string	false	"省份筛选"
+// @Param   City			query	string	false	"城市筛选"
+// @Param   IndustryId		query	int	false	"行业ID"
+// @Param   SortParam		query	int	false	"排序字段: 1-签约时间; 2-到期时间; 3-创建时间; 4-用户上限"
+// @Param   SortType		query	int	false	"排序类型: 1-正序; 2-倒序"
+// @Success 200 Ret=200 获取成功
+// @router /page_list [get]
+func (this *EtaBusinessController) PageList() {
+	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")
+	var startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = utils.StartIndex(currentIndex, pageSize)
+
+	// 权限校验
+	resp := new(eta_business.EtaBusinessListResp)
+	ok, e := etaBusinessService.CheckEtaBusinessOperateAuth(sysUser.RoleTypeCode)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "操作权限校验失败, ErrMsg: " + e.Error()
+		return
+	}
+	if !ok {
+		resp.Paging = paging.GetPaging(currentIndex, pageSize, 0)
+		br.Data = resp
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "获取成功"
+		return
+	}
+
+	cond := ``
+	pars := make([]interface{}, 0)
+	// 筛选项
+	{
+		// 关键词
+		keyword := this.GetString("Keyword", "")
+		keyword = strings.TrimSpace(keyword)
+		if keyword != "" {
+			kw := fmt.Sprint("%", keyword, "%")
+			cond += fmt.Sprintf(` AND (%s LIKE ? OR %s LIKE ? OR %s LIKE ?)`, eta_business.EtaBusinessColumns.BusinessName, eta_business.EtaBusinessColumns.BusinessCode, eta_business.EtaBusinessColumns.CreditCode)
+			pars = append(pars, kw, kw, kw)
+		}
+
+		// 销售
+		sellerIds := this.GetString("SellerIds", "")
+		if sellerIds != "" {
+			arr := strings.Split(sellerIds, ",")
+			ids := make([]int, 0)
+			for _, s := range arr {
+				v, e := strconv.Atoi(s)
+				if e != nil {
+					br.Msg = "销售ID有误"
+					return
+				}
+				ids = append(ids, v)
+			}
+			if len(ids) == 0 {
+				resp.Paging = paging.GetPaging(currentIndex, pageSize, 0)
+				br.Data = resp
+				br.Ret = 200
+				br.Success = true
+				br.Msg = "获取成功"
+				return
+			}
+			cond += fmt.Sprintf(` AND %s IN (%s)`, eta_business.EtaBusinessColumns.SellerId, utils.GetOrmInReplace(len(ids)))
+			pars = append(pars, ids)
+		}
+
+		// 签约状态
+		signingStatus, _ := this.GetInt("SigningStatus", 0)
+		if signingStatus > 0 {
+			cond += fmt.Sprintf(` AND %s = ?`, eta_business.EtaBusinessColumns.SigningStatus)
+			pars = append(pars, signingStatus)
+		}
+
+		// 商家地址
+		province := this.GetString("Province", "")
+		province = strings.TrimSpace(province)
+		if province != "" {
+			provinceArr := strings.Split(province, ",")
+			if len(provinceArr) == 0 {
+				resp.Paging = paging.GetPaging(currentIndex, pageSize, 0)
+				br.Data = resp
+				br.Ret = 200
+				br.Success = true
+				br.Msg = "获取成功"
+				return
+			}
+			cond += fmt.Sprintf(` AND %s IN (%s)`, eta_business.EtaBusinessColumns.Province, utils.GetOrmInReplace(len(provinceArr)))
+			pars = append(pars, provinceArr)
+		}
+		city := this.GetString("City", "")
+		city = strings.TrimSpace(city)
+		if city != "" {
+			cityArr := strings.Split(city, ",")
+			if len(cityArr) == 0 {
+				resp.Paging = paging.GetPaging(currentIndex, pageSize, 0)
+				br.Data = resp
+				br.Ret = 200
+				br.Success = true
+				br.Msg = "获取成功"
+				return
+			}
+			cond += fmt.Sprintf(` AND %s IN (%s)`, eta_business.EtaBusinessColumns.City, utils.GetOrmInReplace(len(cityArr)))
+			pars = append(pars, cityArr)
+		}
+
+		// 行业
+		industryId, _ := this.GetInt("IndustryId", 0)
+		if industryId > 0 {
+			cond += fmt.Sprintf(` AND %s = ?`, eta_business.EtaBusinessColumns.IndustryId)
+			pars = append(pars, industryId)
+		}
+
+		// 国家
+		nation := this.GetString("Nation", "")
+		nation = strings.TrimSpace(nation)
+		if nation != "" {
+			cond += ` AND nation = ? `
+			pars = append(pars, nation)
+		}
+	}
+
+	order := ``
+	// 排序
+	{
+		fieldArr := []int{1, 2, 3, 4}
+		typeArr := []int{1, 2}
+		fieldMap := map[int]string{
+			1: "signing_time",
+			2: "expired_time",
+			3: "create_time",
+			4: "user_max",
+		}
+		typeMap := map[int]string{
+			1: "ASC",
+			2: "DESC",
+		}
+
+		sortParam, _ := this.GetInt("SortParam", 0)
+		sortType, _ := this.GetInt("SortType", 0)
+		if utils.InArrayByInt(fieldArr, sortParam) && utils.InArrayByInt(typeArr, sortType) {
+			order = fmt.Sprintf("%s %s", fieldMap[sortParam], typeMap[sortType])
+		}
+		fmt.Println("order: ", order)
+	}
+
+	// 获取列表
+	businessOb := new(eta_business.EtaBusiness)
+	total, e := businessOb.GetCountByCondition(cond, pars)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取商家总数失败, Err: " + e.Error()
+		return
+	}
+	list, e := businessOb.GetPageItemsByCondition(cond, pars, []string{}, order, startSize, pageSize)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取商家列表失败, Err: " + e.Error()
+		return
+	}
+	items := make([]*eta_business.EtaBusinessItem, 0)
+	for _, v := range list {
+		b := new(eta_business.EtaBusinessItem)
+		b.EtaBusinessId = v.EtaBusinessId
+		b.BusinessName = v.BusinessName
+		b.BusinessCode = v.BusinessCode
+		b.CreditCode = v.CreditCode
+		b.RegionType = v.RegionType
+		b.Province = v.Province
+		b.City = v.City
+		b.Address = v.Address
+		b.SellerId = v.SellerId
+		b.SellerName = v.SellerName
+		b.Leader = v.Leader
+		b.IndustryId = v.IndustryId
+		b.IndustryName = v.IndustryName
+		b.CapitalScale = v.CapitalScale
+		b.ResearchTeamSize = v.ResearchTeamSize
+		b.UserMax = v.UserMax
+		b.SigningStatus = v.SigningStatus
+		b.Enable = v.Enable
+		b.ContractId = v.ContractId
+		b.SigningTime = utils.TimeTransferString(utils.FormatDate, v.SigningTime)
+		b.ExpiredTime = utils.TimeTransferString(utils.FormatDate, v.ExpiredTime)
+		b.CreateTime = v.CreateTime.Format(utils.FormatDateTime)
+		b.ModifyTime = v.ModifyTime.Format(utils.FormatDateTime)
+		b.Nation = v.Nation
+		items = append(items, b)
+	}
+
+	resp.List = items
+	resp.Paging = paging.GetPaging(currentIndex, pageSize, total)
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// Add
+// @Title 新增商家
+// @Description 新增商家
+// @Param	request	body eta_business.EtaBusinessAddReq true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /add [post]
+func (this *EtaBusinessController) Add() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	// 操作权限校验
+	ok, e := etaBusinessService.CheckEtaBusinessOperateAuth(sysUser.RoleTypeCode)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "操作权限校验失败, ErrMsg: " + e.Error()
+		return
+	}
+	if !ok {
+		br.Msg = "无权操作"
+		return
+	}
+
+	// 参数校验
+	var req eta_business.EtaBusinessAddReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	req.BusinessName = strings.TrimSpace(req.BusinessName)
+	if req.BusinessName == "" {
+		br.Msg = "商家名称不可为空"
+		return
+	}
+	if req.CreditCode == "" {
+		br.Msg = "社会统一信用码不可为空"
+		return
+	}
+	req.Province = strings.TrimSpace(req.Province)
+	if req.Nation == "" {
+		if req.Province == "" {
+			br.Msg = "省份不可为空"
+			return
+		}
+		req.City = strings.TrimSpace(req.City)
+		if req.City == "" {
+			br.Msg = "城市不可为空"
+			return
+		}
+	}
+
+	req.Leader = strings.TrimSpace(req.Leader)
+	if req.Leader == "" {
+		br.Msg = "决策人不可为空"
+		return
+	}
+	if req.IndustryId <= 0 || req.IndustryName == "" {
+		br.Msg = "所属行业不可为空"
+		return
+	}
+	if req.ResearchTeamSize == "" {
+		br.Msg = "研究团队规模不可为空"
+		return
+	}
+	/*if req.UserMax <= 0 {
+		br.Msg = "用户上限不可小于0"
+		return
+	}*/
+	req.CapitalScale = strings.TrimSpace(req.CapitalScale)
+
+	// 如果仅校验不新增, 那么不做第二页签约时间的校验(已废弃, 20230919签约时间非必填)
+	var signTime, expiredTime time.Time
+	if !req.IsCheck {
+		if req.SigningTime != "" && req.ExpiredTime != "" {
+			signTime, e = time.ParseInLocation(utils.FormatDate, req.SigningTime, time.Local)
+			if e != nil {
+				br.Msg = "签约时间格式有误"
+				br.ErrMsg = "签约时间格式有误, Err: " + e.Error()
+				return
+			}
+			expiredTime, e = time.ParseInLocation(utils.FormatDate, req.ExpiredTime, time.Local)
+			if e != nil {
+				br.Msg = "到期时间格式有误"
+				br.ErrMsg = "到期时间格式有误, Err: " + e.Error()
+				return
+			}
+			if !expiredTime.After(signTime) {
+				br.Msg = "到期时间不得早于签约时间"
+				return
+			}
+		}
+	}
+
+	// 重名校验
+	{
+		item := new(eta_business.EtaBusiness)
+		cond := fmt.Sprintf(` AND %s = ?`, eta_business.EtaBusinessColumns.BusinessName)
+		pars := make([]interface{}, 0)
+		pars = append(pars, req.BusinessName)
+		exist, e := item.GetItemByCondition(cond, pars)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			br.Msg = "操作失败"
+			br.ErrMsg = "获取重名商家失败, Err: " + e.Error()
+			return
+		}
+		if exist != nil {
+			br.Msg = "商家名称已存在"
+			return
+		}
+	}
+
+	// 社会信用码重复校验
+	{
+		item := new(eta_business.EtaBusiness)
+		cond := fmt.Sprintf(` AND %s = ?`, eta_business.EtaBusinessColumns.CreditCode)
+		pars := make([]interface{}, 0)
+		pars = append(pars, req.CreditCode)
+		exist, e := item.GetItemByCondition(cond, pars)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			br.Msg = "操作失败"
+			br.ErrMsg = "获取重复信用码商家失败, Err: " + e.Error()
+			return
+		}
+		if exist != nil {
+			br.Msg = "商家社会信用码已存在"
+			return
+		}
+	}
+
+	// 如果只做校验不新增, 此处校验通过后直接返回true
+	if req.IsCheck {
+		br.Data = true
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "校验通过"
+		return
+	}
+
+	// 商家编码
+	businessCode, e := eta_business.CreateEtaBusinessCode()
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "生成商家编码失败, Err: " + e.Error()
+		return
+	}
+	codeEncrypt := utils.MD5(fmt.Sprintf("%s%s", businessCode, utils.BusinessCodeSalt))
+
+	now := time.Now().Local()
+	status := eta_business.EtaBusinessSigningStatusWait // 默认待签约
+	// 若当前时间不在首次签约时间内, 也算作已终止, 进入合约期时会由定时任务改为首次签约
+	if !signTime.IsZero() && !expiredTime.IsZero() {
+		if !now.Before(signTime) && !now.After(expiredTime) {
+			status = eta_business.EtaBusinessSigningStatusFirst // 首次签约
+		} else {
+			status = eta_business.EtaBusinessSigningStatusTerminate // 已终止
+		}
+	}
+
+	// 新增商家和签约
+	businessItem := new(eta_business.EtaBusiness)
+	businessItem.BusinessName = req.BusinessName
+	businessItem.BusinessCode = businessCode
+	businessItem.CodeEncrypt = codeEncrypt
+	businessItem.CreditCode = req.CreditCode
+	businessItem.RegionType = req.RegionType
+	businessItem.Province = req.Province
+	businessItem.City = req.City
+	businessItem.Address = req.Province + req.City
+	businessItem.SellerId = req.SellerId
+	businessItem.SellerName = req.SellerName
+	businessItem.Leader = req.Leader
+	businessItem.IndustryId = req.IndustryId
+	businessItem.IndustryName = req.IndustryName
+	businessItem.CapitalScale = req.CapitalScale
+	businessItem.ResearchTeamSize = req.ResearchTeamSize
+	//businessItem.UserMax = req.UserMax
+	businessItem.Enable = 1
+	businessItem.SigningStatus = status
+	businessItem.SigningTime = signTime
+	businessItem.ExpiredTime = expiredTime
+	businessItem.CreateTime = now
+	businessItem.ModifyTime = now
+	businessItem.Nation = req.Nation
+	contractItem := new(eta_business.EtaBusinessContract)
+	if !signTime.IsZero() && !expiredTime.IsZero() {
+		contractItem.SigningTime = signTime
+		contractItem.ExpiredTime = expiredTime
+		contractItem.IsFirst = 1
+		contractItem.CreateTime = now
+		contractItem.ModifyTime = now
+	} else {
+		contractItem = nil
+	}
+	if e = eta_business.CreateEtaBusinessAndContract(businessItem, contractItem); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "新增商家和签约失败, Err: " + e.Error()
+		return
+	}
+
+	// 给商家帮助文档所有分类的权限
+	if e = help_doc.AddAllHelpDocClassifyVisible(businessItem.EtaBusinessId); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "新增商家分类文档权限失败, Err: " + e.Error()
+		return
+	}
+	resp := new(eta_business.EtaBusinessAddResp)
+	resp.EtaBusinessId = businessItem.EtaBusinessId
+	// 操作日志
+	go func() {
+		recordOb := new(eta_business.EtaBusinessOperationRecord)
+		recordOb.EtaBusinessId = businessItem.EtaBusinessId
+		recordOb.SellerId = businessItem.SellerId
+		recordOb.SysUserId = sysUser.AdminId
+		recordOb.SysRealName = sysUser.RealName
+		recordOb.OperationType = eta_business.EtaBusinessOperationTypeAdd
+		recordOb.OperationRemark = fmt.Sprintf("%s新增商户", sysUser.RealName)
+		recordOb.CreateTime = time.Now().Local()
+		_ = recordOb.Create()
+	}()
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+	br.Data = resp
+}
+
+// Edit
+// @Title 编辑商家
+// @Description 编辑商家
+// @Param	request	body eta_business.EtaBusinessEditReq true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /edit [post]
+func (this *EtaBusinessController) Edit() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req eta_business.EtaBusinessEditReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	if req.EtaBusinessId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = "参数有误, 商家ID为空"
+		return
+	}
+	if req.Nation == "" {
+		req.Province = strings.TrimSpace(req.Province)
+		if req.Province == "" {
+			br.Msg = "省份不可为空"
+			return
+		}
+		req.City = strings.TrimSpace(req.City)
+		if req.City == "" {
+			br.Msg = "城市不可为空"
+			return
+		}
+	}
+
+	req.Leader = strings.TrimSpace(req.Leader)
+	if req.Leader == "" {
+		br.Msg = "决策人不可为空"
+		return
+	}
+	if req.IndustryId <= 0 || req.IndustryName == "" {
+		br.Msg = "所属行业不可为空"
+		return
+	}
+	if req.ResearchTeamSize == "" {
+		br.Msg = "研究团队规模不可为空"
+		return
+	}
+	/*if req.UserMax <= 0 {
+		br.Msg = "用户上限不可小于0"
+		return
+	}*/
+	req.CapitalScale = strings.TrimSpace(req.CapitalScale)
+
+	// 权限校验
+	ok, e := etaBusinessService.CheckEtaBusinessOperateAuth(sysUser.RoleTypeCode)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "操作权限校验失败, ErrMsg: " + e.Error()
+		return
+	}
+	if !ok {
+		br.Msg = "无权操作"
+		return
+	}
+
+	ob := new(eta_business.EtaBusiness)
+	item, e := ob.GetItemById(req.EtaBusinessId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "商家不存在, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取商家信息失败, Err: " + e.Error()
+		return
+	}
+	item.Province = req.Province
+	item.City = req.City
+	item.Address = req.Province + req.City
+	item.Leader = req.Leader
+	item.IndustryId = req.IndustryId
+	item.IndustryName = req.IndustryName
+	item.CapitalScale = req.CapitalScale
+	item.ResearchTeamSize = req.ResearchTeamSize
+	//item.UserMax = req.UserMax
+	item.Nation = req.Nation
+	item.ModifyTime = time.Now().Local()
+	cols := []string{
+		"Province", "City", "Address", "Leader", "IndustryId", "IndustryName", "CapitalScale", "ResearchTeamSize", "ModifyTime",
+	}
+	if e := item.Update(cols); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "编辑商家失败, Err: " + e.Error()
+		return
+	}
+
+	// 操作日志
+	go func() {
+		recordOb := new(eta_business.EtaBusinessOperationRecord)
+		recordOb.EtaBusinessId = item.EtaBusinessId
+		recordOb.SellerId = item.SellerId
+		recordOb.SysUserId = sysUser.AdminId
+		recordOb.SysRealName = sysUser.RealName
+		recordOb.OperationType = eta_business.EtaBusinessOperationTypeEdit
+		recordOb.OperationRemark = fmt.Sprintf("%s编辑商户", sysUser.RealName)
+		recordOb.CreateTime = time.Now().Local()
+		_ = recordOb.Create()
+	}()
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// Signing
+// @Title 签约续约
+// @Description 签约续约(业务上仅用于续约, 兼容首次签约)
+// @Param	request	body eta_business.EtaBusinessSigningReq true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /signing [post]
+func (this *EtaBusinessController) Signing() {
+	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 eta_business.EtaBusinessSigningReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	if req.EtaBusinessId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = "参数有误, 商家ID为空"
+		return
+	}
+	if req.SigningTime == "" {
+		br.Msg = "签约时间不可为空"
+		return
+	}
+	if req.ExpiredTime == "" {
+		br.Msg = "到期时间不可为空"
+		return
+	}
+	signTime, e := time.ParseInLocation(utils.FormatDate, req.SigningTime, time.Local)
+	if e != nil {
+		br.Msg = "签约时间格式有误"
+		br.ErrMsg = "签约时间格式有误, Err: " + e.Error()
+		return
+	}
+	expiredTime, e := time.ParseInLocation(utils.FormatDate, req.ExpiredTime, time.Local)
+	if e != nil {
+		br.Msg = "到期时间格式有误"
+		br.ErrMsg = "到期时间格式有误, Err: " + e.Error()
+		return
+	}
+	if !expiredTime.After(signTime) {
+		br.Msg = "到期时间不得早于签约时间"
+		return
+	}
+
+	// 权限校验
+	ok, e := etaBusinessService.CheckEtaBusinessOperateAuth(sysUser.RoleTypeCode)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "操作权限校验失败, ErrMsg: " + e.Error()
+		return
+	}
+	if !ok {
+		br.Msg = "无权操作"
+		return
+	}
+
+	// 获取商家信息
+	businessOb := new(eta_business.EtaBusiness)
+	business, e := businessOb.GetItemById(req.EtaBusinessId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "商家不存在, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取商家信息失败, Err: " + e.Error()
+		return
+	}
+
+	// 续约不可早于当前生效合约的到期时间
+	if business.ContractId > 0 {
+		{
+			ob := new(eta_business.EtaBusinessContract)
+			item, e := ob.GetItemById(business.ContractId)
+			if e != nil {
+				br.Msg = "操作失败"
+				br.ErrMsg = "获取商家当前合同失败, Err: " + e.Error()
+				return
+			}
+			if !signTime.After(item.ExpiredTime) {
+				br.Msg = "签约时间不可早于当前合同的到期时间"
+				return
+			}
+		}
+	}
+
+	// 获取历史签约, 签约日期不可交叠
+	contract := new(eta_business.EtaBusinessContract)
+	cond := fmt.Sprintf(` AND %s = ?`, eta_business.EtaBusinessContractColumns.EtaBusinessId)
+	pars := make([]interface{}, 0)
+	pars = append(pars, req.EtaBusinessId)
+	contracts, e := contract.GetItemsByCondition(cond, pars, []string{}, "signing_time ASC")
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取商家签约列表失败, Err: " + e.Error()
+		return
+	}
+
+	// 校验签约时间是否重叠
+	isFirst := true      // 是否为首次签约
+	changeFirst := false // 是否需要更新首次签约合同
+	if len(contracts) > 0 {
+		isFirst = false
+		for k, c := range contracts {
+			pass := false
+			if expiredTime.Before(c.SigningTime) {
+				pass = true
+			}
+			if signTime.After(c.ExpiredTime) {
+				pass = true
+			}
+			if !pass {
+				br.Msg = "签约时间在存续期内, 请检查"
+				return
+			}
+			// 若该合同签约时间早于第一份合同(业务上未限制所以可能会出现这种情况, contracts已排序), 则更新为首次签约合同
+			if k == 0 && signTime.Before(c.SigningTime) {
+				changeFirst = true
+				isFirst = true
+			}
+		}
+	}
+
+	// 新增签约
+	contract.EtaBusinessId = req.EtaBusinessId
+	contract.SigningTime = signTime
+	contract.ExpiredTime = expiredTime
+	contract.CreateTime = time.Now().Local()
+	contract.ModifyTime = time.Now().Local()
+	if isFirst {
+		contract.IsFirst = 1
+	}
+	if e = contract.CreateMaybeUpdateFirst(contract, changeFirst); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "新增签约失败, Err: " + e.Error()
+		return
+	}
+
+	// 签约后续操作
+	go func() {
+		_ = etaBusinessService.UpdateEtaBusinessAfterSigning(business.EtaBusinessId)
+	}()
+
+	// 续约操作日志
+	if !isFirst {
+		go func() {
+			recordOb := new(eta_business.EtaBusinessOperationRecord)
+			recordOb.EtaBusinessId = business.EtaBusinessId
+			recordOb.SellerId = business.SellerId
+			recordOb.SysUserId = sysUser.AdminId
+			recordOb.SysRealName = sysUser.RealName
+			recordOb.OperationType = eta_business.EtaBusinessOperationTypeRenewalContract
+			recordOb.OperationRemark = fmt.Sprintf("%s添加续约", sysUser.RealName)
+			recordOb.CreateTime = time.Now().Local()
+			_ = recordOb.Create()
+		}()
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// Enable
+// @Title 禁用启用
+// @Description 禁用启用
+// @Param	request	body eta_business.EtaBusinessEnableReq true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /enable [post]
+func (this *EtaBusinessController) Enable() {
+	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 eta_business.EtaBusinessEnableReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	if req.EtaBusinessId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = "参数有误, 商家ID为空"
+		return
+	}
+
+	// 权限校验
+	ok, e := etaBusinessService.CheckEtaBusinessOperateAuth(sysUser.RoleTypeCode)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "操作权限校验失败, ErrMsg: " + e.Error()
+		return
+	}
+	if !ok {
+		br.Msg = "无权操作"
+		return
+	}
+
+	// 获取商家信息
+	businessOb := new(eta_business.EtaBusiness)
+	business, e := businessOb.GetItemById(req.EtaBusinessId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "商家不存在, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取商家信息失败, Err: " + e.Error()
+		return
+	}
+
+	// 禁启用
+	typeMap := map[int]int{
+		0: eta_business.EtaBusinessOperationTypeEnable,
+		1: eta_business.EtaBusinessOperationTypeDisable,
+	}
+	typeRemarkMap := map[int]string{
+		0: "启用",
+		1: "禁用",
+	}
+	operateType := typeMap[business.Enable]
+	operateRemark := typeRemarkMap[business.Enable]
+
+	business.Enable = business.Enable ^ 1
+	business.ModifyTime = time.Now().Local()
+	cols := []string{"Enable", "ModifyTime"}
+	if e = business.Update(cols); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "更新商家状态失败, Err: " + e.Error()
+		return
+	}
+
+	// 操作日志
+	go func() {
+		recordOb := new(eta_business.EtaBusinessOperationRecord)
+		recordOb.EtaBusinessId = business.EtaBusinessId
+		recordOb.SellerId = business.SellerId
+		recordOb.SysUserId = sysUser.AdminId
+		recordOb.SysRealName = sysUser.RealName
+		recordOb.OperationType = operateType
+		recordOb.OperationRemark = fmt.Sprintf("%s%s商户", sysUser.RealName, operateRemark)
+		recordOb.CreateTime = time.Now().Local()
+		_ = recordOb.Create()
+	}()
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// MoveSeller
+// @Title 移动销售
+// @Description 移动销售
+// @Param	request	body eta_business.EtaBusinessMoveSellerReq true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /move_seller [post]
+func (this *EtaBusinessController) MoveSeller() {
+	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 eta_business.EtaBusinessMoveSellerReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	if req.EtaBusinessId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = "参数有误, 商家ID为空"
+		return
+	}
+	if req.SellerId <= 0 || req.SellerName == "" {
+		br.Msg = "销售不可为空"
+		return
+	}
+
+	// 权限校验
+	ok, e := etaBusinessService.CheckEtaBusinessOperateAuth(sysUser.RoleTypeCode)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "操作权限校验失败, ErrMsg: " + e.Error()
+		return
+	}
+	if !ok {
+		br.Msg = "无权操作"
+		return
+	}
+
+	// 获取商家信息
+	businessOb := new(eta_business.EtaBusiness)
+	business, e := businessOb.GetItemById(req.EtaBusinessId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "商家不存在, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取商家信息失败, Err: " + e.Error()
+		return
+	}
+
+	// 更新销售
+	business.SellerId = req.SellerId
+	business.SellerName = req.SellerName
+	business.ModifyTime = time.Now().Local()
+	cols := []string{"SellerId", "SellerName", "ModifyTime"}
+	if e = business.Update(cols); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "移动至销售失败, Err: " + e.Error()
+		return
+	}
+
+	// 操作日志
+	go func() {
+		recordOb := new(eta_business.EtaBusinessOperationRecord)
+		recordOb.EtaBusinessId = business.EtaBusinessId
+		recordOb.SellerId = req.SellerId
+		recordOb.SysUserId = sysUser.AdminId
+		recordOb.SysRealName = sysUser.RealName
+		recordOb.OperationType = eta_business.EtaBusinessOperationTypeMoveSeller
+		recordOb.OperationRemark = fmt.Sprintf("%s移动到: %s", sysUser.RealName, req.SellerName)
+		recordOb.CreateTime = time.Now().Local()
+		_ = recordOb.Create()
+	}()
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// Detail
+// @Title 商家详情
+// @Description 商家详情
+// @Param   EtaBusinessId	query	int		true	"商家ID"
+// @Success 200 Ret=200 获取成功
+// @router /detail [get]
+func (this *EtaBusinessController) Detail() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	// 权限校验
+	ok, e := etaBusinessService.CheckEtaBusinessOperateAuth(sysUser.RoleTypeCode)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "操作权限校验失败, ErrMsg: " + e.Error()
+		return
+	}
+	if !ok {
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "获取成功"
+		return
+	}
+	businessId, _ := this.GetInt("EtaBusinessId", 0)
+	if businessId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = "参数有误, 商家ID"
+		return
+	}
+
+	businessOb := new(eta_business.EtaBusiness)
+	item, e := businessOb.GetItemById(businessId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "商家不存在, 请刷新页面"
+			return
+		}
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取商家信息失败, Err: " + e.Error()
+		return
+	}
+
+	resp := new(eta_business.EtaBusinessItem)
+	resp.EtaBusinessId = item.EtaBusinessId
+	resp.BusinessName = item.BusinessName
+	resp.BusinessCode = item.BusinessCode
+	resp.CreditCode = item.CreditCode
+	resp.RegionType = item.RegionType
+	resp.Province = item.Province
+	resp.City = item.City
+	resp.Address = item.Address
+	resp.SellerId = item.SellerId
+	resp.SellerName = item.SellerName
+	resp.Leader = item.Leader
+	resp.IndustryId = item.IndustryId
+	resp.IndustryName = item.IndustryName
+	resp.CapitalScale = item.CapitalScale
+	resp.ResearchTeamSize = item.ResearchTeamSize
+	resp.UserMax = item.UserMax
+	resp.SigningStatus = item.SigningStatus
+	resp.Enable = item.Enable
+	resp.ContractId = item.ContractId
+	resp.SigningTime = utils.TimeTransferString(utils.FormatDate, item.SigningTime)
+	resp.ExpiredTime = utils.TimeTransferString(utils.FormatDate, item.ExpiredTime)
+	resp.CreateTime = item.CreateTime.Format(utils.FormatDateTime)
+	resp.ModifyTime = item.ModifyTime.Format(utils.FormatDateTime)
+	resp.Nation = item.Nation
+
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// ContractList
+// @Title 商家签约列表
+// @Description 商家签约列表
+// @Param   EtaBusinessId	query	int		true	"商家ID"
+// @Success 200 Ret=200 获取成功
+// @router /contract_list [get]
+func (this *EtaBusinessController) ContractList() {
+	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
+	}
+	// 权限校验
+	resp := make([]*eta_business.EtaBusinessContractItem, 0)
+	ok, e := etaBusinessService.CheckEtaBusinessOperateAuth(sysUser.RoleTypeCode)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "操作权限校验失败, ErrMsg: " + e.Error()
+		return
+	}
+	if !ok {
+		br.Data = resp
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "获取成功"
+		return
+	}
+	businessId, _ := this.GetInt("EtaBusinessId", 0)
+	if businessId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = "参数有误, 商家ID"
+		return
+	}
+
+	cond := fmt.Sprintf(` AND %s = ?`, eta_business.EtaBusinessContractColumns.EtaBusinessId)
+	pars := make([]interface{}, 0)
+	pars = append(pars, businessId)
+	order := `signing_time ASC`
+
+	contractOb := new(eta_business.EtaBusinessContract)
+	list, e := contractOb.GetItemsByCondition(cond, pars, []string{}, order)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取商家签约列表失败, Err: " + e.Error()
+		return
+	}
+	now := time.Now().Local()
+	for _, v := range list {
+		b := new(eta_business.EtaBusinessContractItem)
+		b.EtaBusinessContractId = v.EtaBusinessContractId
+		b.EtaBusinessId = v.EtaBusinessId
+		b.SigningTime = utils.TimeTransferString(utils.FormatDate, v.SigningTime)
+		b.ExpiredTime = utils.TimeTransferString(utils.FormatDate, v.ExpiredTime)
+		// 到期天数, 终止日按当天的23:59:59算
+		strEnd := v.ExpiredTime.Format(utils.FormatDate)
+		strEnd = strEnd + " 23:59:59"
+		endTime, e := time.ParseInLocation(utils.FormatDateTime, strEnd, time.Local)
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "签约日期有误, Err: " + e.Error()
+			return
+		}
+		b.ExpireDay = "-"
+		if now.After(v.SigningTime) && now.Before(endTime) {
+			diff := utils.GetDiffDays(v.ExpiredTime, now)
+			b.ExpireDay = strconv.Itoa(diff)
+			b.Using = true
+		}
+		resp = append(resp, b)
+	}
+
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// OperateRecordList
+// @Title 操作日志列表
+// @Description 操作日志列表
+// @Param   EtaBusinessId	query	int		true	"商家ID"
+// @Success 200 Ret=200 获取成功
+// @router /operate_record_list [get]
+func (this *EtaBusinessController) OperateRecordList() {
+	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
+	}
+	// 权限校验
+	resp := make([]*eta_business.EtaBusinessOperationRecordItem, 0)
+	ok, e := etaBusinessService.CheckEtaBusinessOperateAuth(sysUser.RoleTypeCode)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "操作权限校验失败, ErrMsg: " + e.Error()
+		return
+	}
+	if !ok {
+		br.Data = resp
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "获取成功"
+		return
+	}
+	businessId, _ := this.GetInt("EtaBusinessId", 0)
+	if businessId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = "参数有误, 商家ID"
+		return
+	}
+
+	cond := fmt.Sprintf(` AND %s = ?`, eta_business.EtaBusinessOperationRecordColumns.EtaBusinessId)
+	pars := make([]interface{}, 0)
+	pars = append(pars, businessId)
+
+	recordOb := new(eta_business.EtaBusinessOperationRecord)
+	list, e := recordOb.GetItemsByCondition(cond, pars, []string{}, "")
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取商家操作日志列表失败, Err: " + e.Error()
+		return
+	}
+	for _, v := range list {
+		r := new(eta_business.EtaBusinessOperationRecordItem)
+		r.EtaBusinessId = v.EtaBusinessId
+		r.SysUserId = v.SysUserId
+		r.SysRealName = v.SysRealName
+		r.OperationType = v.OperationType
+		r.OperationRemark = v.OperationRemark
+		r.CreateTime = v.CreateTime.Format(utils.FormatDateTime)
+		resp = append(resp, r)
+	}
+
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// EditSign
+// @Title 编辑签约
+// @Description 编辑签约
+// @Param	request	body eta_business.EtaBusinessEditSignReq true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /edit_sign [post]
+func (this *EtaBusinessController) EditSign() {
+	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 eta_business.EtaBusinessEditSignReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	if req.EtaBusinessContractId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = "参数有误, 商家签约ID为空"
+		return
+	}
+	if req.SigningTime == "" {
+		br.Msg = "签约时间不可为空"
+		return
+	}
+	if req.ExpiredTime == "" {
+		br.Msg = "到期时间不可为空"
+		return
+	}
+	signTime, e := time.ParseInLocation(utils.FormatDate, req.SigningTime, time.Local)
+	if e != nil {
+		br.Msg = "签约时间格式有误"
+		br.ErrMsg = "签约时间格式有误, Err: " + e.Error()
+		return
+	}
+	expiredTime, e := time.ParseInLocation(utils.FormatDate, req.ExpiredTime, time.Local)
+	if e != nil {
+		br.Msg = "到期时间格式有误"
+		br.ErrMsg = "到期时间格式有误, Err: " + e.Error()
+		return
+	}
+	if !expiredTime.After(signTime) {
+		br.Msg = "到期时间不得早于签约时间"
+		return
+	}
+
+	// 权限校验
+	ok, e := etaBusinessService.CheckEtaBusinessOperateAuth(sysUser.RoleTypeCode)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "操作权限校验失败, ErrMsg: " + e.Error()
+		return
+	}
+	if !ok {
+		br.Msg = "无权操作"
+		return
+	}
+
+	contractOb := new(eta_business.EtaBusinessContract)
+	contract, e := contractOb.GetItemById(req.EtaBusinessContractId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "签约信息不存在, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取商家签约信息失败, Err: " + e.Error()
+		return
+	}
+	businessOb := new(eta_business.EtaBusiness)
+	business, e := businessOb.GetItemById(contract.EtaBusinessId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "商家不存在, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取商家信息失败, Err: " + e.Error()
+		return
+	}
+
+	// 续约不可早于当前生效合约的到期时间
+	if business.ContractId > 0 && business.ContractId != req.EtaBusinessContractId {
+		{
+			ob := new(eta_business.EtaBusinessContract)
+			item, e := ob.GetItemById(business.ContractId)
+			if e != nil {
+				br.Msg = "操作失败"
+				br.ErrMsg = "获取商家当前合同失败, Err: " + e.Error()
+				return
+			}
+			if !signTime.After(item.ExpiredTime) {
+				br.Msg = "签约时间不可早于当前合同的到期时间"
+				return
+			}
+		}
+	}
+
+	// 获取历史签约, 签约日期不可交叠
+	cond := fmt.Sprintf(` AND %s = ?`, eta_business.EtaBusinessContractColumns.EtaBusinessId)
+	pars := make([]interface{}, 0)
+	pars = append(pars, contract.EtaBusinessId)
+	contracts, e := contractOb.GetItemsByCondition(cond, pars, []string{}, "signing_time ASC")
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取商家签约列表失败, Err: " + e.Error()
+		return
+	}
+
+	// 校验签约时间是否重叠
+	isFirst := true      // 是否为首次签约
+	changeFirst := false // 是否需要更新首次签约合同
+	if len(contracts) > 0 {
+		isFirst = false
+		for k, c := range contracts {
+			if c.EtaBusinessContractId == req.EtaBusinessContractId {
+				continue
+			}
+			pass := false
+			if expiredTime.Before(c.SigningTime) {
+				pass = true
+			}
+			if signTime.After(c.ExpiredTime) {
+				pass = true
+			}
+			if !pass {
+				br.Msg = "签约时间在存续期内, 请检查"
+				return
+			}
+			// 若该合同签约时间早于第一份合同(业务上未限制所以可能会出现这种情况, contracts已排序), 则更新为首次签约合同
+			if k == 0 && signTime.Before(c.SigningTime) {
+				changeFirst = true
+				isFirst = true
+			}
+		}
+	}
+
+	// 新增签约
+	contract.SigningTime = signTime
+	contract.ExpiredTime = expiredTime
+	contract.ModifyTime = time.Now().Local()
+	if isFirst {
+		contract.IsFirst = 1
+	}
+	updateCols := []string{"SigningTime", "ExpiredTime", "ModifyTime"}
+	if e = contract.UpdateMaybeUpdateFirst(contract, changeFirst, updateCols); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "编辑签约失败, Err: " + e.Error()
+		return
+	}
+
+	// 签约后续操作
+	go func() {
+		_ = etaBusinessService.UpdateEtaBusinessAfterSigning(business.EtaBusinessId)
+	}()
+
+	// 续约操作日志
+	if !isFirst {
+		go func() {
+			recordOb := new(eta_business.EtaBusinessOperationRecord)
+			recordOb.EtaBusinessId = business.EtaBusinessId
+			recordOb.SellerId = business.SellerId
+			recordOb.SysUserId = sysUser.AdminId
+			recordOb.SysRealName = sysUser.RealName
+			recordOb.OperationType = eta_business.EtaBusinessOperationTypeEditContract
+			recordOb.OperationRemark = fmt.Sprintf("%s编辑续约", sysUser.RealName)
+			recordOb.CreateTime = time.Now().Local()
+			_ = recordOb.Create()
+		}()
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// RemoveSign
+// @Title 删除签约
+// @Description 删除签约
+// @Param	request	body eta_business.EtaBusinessRemoveSignReq true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /remove_sign [post]
+func (this *EtaBusinessController) RemoveSign() {
+	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 eta_business.EtaBusinessRemoveSignReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	if req.EtaBusinessContractId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = "参数有误, 商家签约ID为空"
+		return
+	}
+
+	// 权限校验
+	ok, e := etaBusinessService.CheckEtaBusinessOperateAuth(sysUser.RoleTypeCode)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "操作权限校验失败, ErrMsg: " + e.Error()
+		return
+	}
+	if !ok {
+		br.Msg = "无权操作"
+		return
+	}
+
+	// 获取商家信息
+	contractOb := new(eta_business.EtaBusinessContract)
+	contract, e := contractOb.GetItemById(req.EtaBusinessContractId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "签约信息不存在, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取商家签约信息失败, Err: " + e.Error()
+		return
+	}
+
+	// 获取商家信息
+	businessOb := new(eta_business.EtaBusiness)
+	business, e := businessOb.GetItemById(contract.EtaBusinessId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "商家不存在, 请检查"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取商家信息失败, Err: " + e.Error()
+		return
+	}
+
+	// 删除签约
+	if e = contract.Del(); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "删除签约失败, Err: " + e.Error()
+		return
+	}
+
+	// 签约后续操作
+	go func() {
+		_ = etaBusinessService.UpdateEtaBusinessAfterSigning(business.EtaBusinessId)
+	}()
+
+	// 操作日志
+	go func() {
+		recordOb := new(eta_business.EtaBusinessOperationRecord)
+		recordOb.EtaBusinessId = business.EtaBusinessId
+		recordOb.SellerId = business.SellerId
+		recordOb.SysUserId = sysUser.AdminId
+		recordOb.SysRealName = sysUser.RealName
+		recordOb.OperationType = eta_business.EtaBusinessOperationTypeRemoveContract
+		recordOb.OperationRemark = fmt.Sprintf("%s删除签约", sysUser.RealName)
+		recordOb.CreateTime = time.Now().Local()
+		_ = recordOb.Create()
+	}()
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// @Title 设置社区图表分类权限
+// @Description 设置社区图表分类权限
+// @Param	request	body eta_business.AddBusinessChartClassifyPermissionReq true "type json string"
+// @Success 200 {object} system.SysRoleListResp
+// @router /add_chart_permission [post]
+func (this *EtaBusinessController) AddChartPermission() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		return
+	}
+	var req eta_business.AddBusinessChartClassifyPermissionReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.EtaBusinessId <= 0 {
+		br.Msg = "请选择商户"
+		return
+	}
+
+	// 权限校验
+	ok, e := etaBusinessService.CheckEtaBusinessOperateAuth(sysUser.RoleTypeCode)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "操作权限校验失败, ErrMsg: " + e.Error()
+		return
+	}
+	if !ok {
+		br.Msg = "无权操作"
+		return
+	}
+
+	ob := new(eta_business.EtaBusiness)
+	businessInfo, e := ob.GetItemById(req.EtaBusinessId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "商家不存在, 请刷新页面"
+			return
+		}
+		br.Msg = "获取商家信息失败"
+		br.ErrMsg = "获取商家信息失败, Err: " + e.Error()
+		return
+	}
+	chartClassifyIdArr := strings.Split(req.ChartClassifyIdStr, ",")
+	addList := make([]*eta_business.BusinessChartClassifyPermission, 0)
+	for _, v := range chartClassifyIdArr {
+		if v == "" {
+			continue
+		}
+		classifyId, e := strconv.Atoi(v)
+		if e != nil {
+			br.Msg = "设置社区权限失败"
+			br.ErrMsg = "classifyId 解析失败,Err:" + e.Error()
+			return
+		}
+		//item := new(system.SysRoleMenu)
+		item := new(eta_business.BusinessChartClassifyPermission)
+		item.ChartClassifyId = classifyId
+		item.BusinessCode = businessInfo.BusinessCode
+		item.EtaBusinessId = req.EtaBusinessId
+		item.CreateTime = time.Now()
+		item.ModifyTime = time.Now()
+		item.AdminId = sysUser.AdminId
+		item.Source = 1
+		addList = append(addList, item)
+	}
+
+	err = eta_business.AddBusinessChartClassifyPermission(addList, businessInfo.BusinessCode)
+	if err != nil {
+		br.Msg = "设置社区权限失败"
+		br.ErrMsg = "设置社区权限失败,Err:" + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.IsAddLog = true
+	br.Msg = "设置成功"
+}
+
+// @Title 获取社区图表分类权限
+// @Description 获取社区图表分类权限
+// @Param	request	body eta_business.AddBusinessChartClassifyPermissionReq true "type json string"
+// @Success 200 {object} system.SysRoleListResp
+// @router /get_chart_permission [get]
+func (this *EtaBusinessController) GetChartPermission() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		return
+	}
+	keyword := this.GetString("Keyword")
+	etaBusinessId, _ := this.GetInt("EtaBusinessId")
+	if etaBusinessId <= 0 {
+		br.Msg = "请选择商户"
+		return
+	}
+	// 权限校验
+	ok, e := etaBusinessService.CheckEtaBusinessOperateAuth(sysUser.RoleTypeCode)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "操作权限校验失败, ErrMsg: " + e.Error()
+		return
+	}
+	if !ok {
+		br.Msg = "无权操作"
+		return
+	}
+
+	ob := new(eta_business.EtaBusiness)
+	businessInfo, e := ob.GetItemById(etaBusinessId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "商家不存在, 请刷新页面"
+			return
+		}
+		br.Msg = "获取商家信息失败"
+		br.ErrMsg = "获取商家信息失败, Err: " + e.Error()
+		return
+	}
+	permissionList, err := eta_business.GetBusinessChartClassifyPermission(businessInfo.EtaBusinessId)
+	if err != nil {
+		br.Msg = "获取社区权限失败"
+		br.ErrMsg = "获取社区权限失败,Err:" + err.Error()
+		return
+	}
+	permissionClassifyIdsMap := make(map[int]struct{})
+	checkList := make([]int, 0)
+	for _, v := range permissionList {
+		if _, ok1 := permissionClassifyIdsMap[v.ChartClassifyId]; !ok1 {
+			permissionClassifyIdsMap[v.ChartClassifyId] = struct{}{}
+			checkList = append(checkList, v.ChartClassifyId)
+		}
+	}
+	allList := make([]*models.ChartClassifyItems, 0)
+	keywordClassifyIdMap := make(map[int]struct{})
+	keywordClassifyIds := make([]int, 0)
+	if keyword != "" {
+		keywordList, e := models.GetChartClassifyListByKeyword(keyword)
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取失败,Err:" + e.Error()
+			return
+		}
+		for _, v := range keywordList {
+			if _, ok1 := keywordClassifyIdMap[v.ChartClassifyId]; !ok1 {
+				keywordClassifyIdMap[v.ChartClassifyId] = struct{}{}
+				// 解析当前的父级路径,找到路径下的所有分类
+				if v.LevelPath == "" {
+					br.Msg = "获取失败"
+					br.ErrMsg = "获取失败,Err: 分类的父级路径为空"
+					return
+				}
+				paths := strings.Split(v.LevelPath, ",")
+				for _, path := range paths {
+					if path != "" {
+						pathInt, _ := strconv.Atoi(path)
+						if _, ok2 := keywordClassifyIdMap[pathInt]; !ok2 {
+							keywordClassifyIdMap[pathInt] = struct{}{}
+						}
+					}
+				}
+			}
+		}
+		for k := range keywordClassifyIdMap {
+			keywordClassifyIds = append(keywordClassifyIds, k)
+		}
+		if len(keywordClassifyIds) > 0 {
+			allList, err = models.GetChartClassifyItemsByIdList(keywordClassifyIds)
+			if err != nil && err.Error() != utils.ErrNoRow() {
+				br.Msg = "获取失败"
+				br.ErrMsg = "获取数据失败,Err:" + err.Error()
+				return
+			}
+		}
+	} else {
+		allList, err = models.GetChartClassify()
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取数据失败,Err:" + err.Error()
+			return
+		}
+	}
+
+	nodeAll := make([]*models.ChartClassifyItems, 0)
+	list := make([]*models.ChartClassifyItems, 0)
+	var sortList models.ChartClassifyItemList
+
+	if len(allList) > 0 {
+		nodeAll = services.GetClassifyTreeRecursive(allList, 0)
+		//根据sort值排序
+		sortList = nodeAll
+		sort.Sort(sortList)
+	}
+
+	list = append(list, sortList...)
+	resp := new(models.BusinessChartClassifyResp)
+	resp.List = list
+	resp.CheckList = checkList
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// PageList
+// @Title 商家列表
+// @Description 商家列表
+// @Param   Keyword			query	string	false	"关键词: 商家名称/社会信用码/商家编码"
+// @Success 200 Ret=200 获取成功
+// @router /list [get]
+func (this *EtaBusinessController) AllList() {
+	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
+	}
+
+	// 权限校验
+	resp := new(eta_business.EtaBusinessAllListResp)
+	ok, e := etaBusinessService.CheckEtaBusinessOperateAuth(sysUser.RoleTypeCode)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "操作权限校验失败, ErrMsg: " + e.Error()
+		return
+	}
+	if !ok {
+		resp.List = make([]*eta_business.EtaBusinessItem, 0)
+		br.Data = resp
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "获取成功"
+		return
+	}
+
+	cond := ``
+	pars := make([]interface{}, 0)
+	// 筛选项
+	{
+		// 关键词
+		keyword := this.GetString("Keyword", "")
+		keyword = strings.TrimSpace(keyword)
+		if keyword != "" {
+			kw := fmt.Sprint("%", keyword, "%")
+			cond += fmt.Sprintf(` AND (%s LIKE ? OR %s LIKE ? OR %s LIKE ?)`, eta_business.EtaBusinessColumns.BusinessName, eta_business.EtaBusinessColumns.BusinessCode, eta_business.EtaBusinessColumns.CreditCode)
+			pars = append(pars, kw, kw, kw)
+		}
+	}
+
+	order := ``
+	// 排序
+	{
+		fieldArr := []int{1, 2, 3, 4}
+		typeArr := []int{1, 2}
+		fieldMap := map[int]string{
+			1: "signing_time",
+			2: "expired_time",
+			3: "create_time",
+			4: "user_max",
+		}
+		typeMap := map[int]string{
+			1: "ASC",
+			2: "DESC",
+		}
+
+		sortParam, _ := this.GetInt("SortParam", 0)
+		sortType, _ := this.GetInt("SortType", 0)
+		if utils.InArrayByInt(fieldArr, sortParam) && utils.InArrayByInt(typeArr, sortType) {
+			order = fmt.Sprintf("%s %s", fieldMap[sortParam], typeMap[sortType])
+		}
+		fmt.Println("order: ", order)
+	}
+
+	// 获取列表
+	businessOb := new(eta_business.EtaBusiness)
+	list, e := businessOb.GetItemsByCondition(cond, pars, []string{}, order)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取商家列表失败, Err: " + e.Error()
+		return
+	}
+	items := make([]*eta_business.EtaBusinessItem, 0)
+	for _, v := range list {
+		b := new(eta_business.EtaBusinessItem)
+		b.EtaBusinessId = v.EtaBusinessId
+		b.BusinessName = v.BusinessName
+		b.BusinessCode = v.BusinessCode
+		b.CreditCode = v.CreditCode
+		b.RegionType = v.RegionType
+		b.Province = v.Province
+		b.City = v.City
+		b.Address = v.Address
+		b.SellerId = v.SellerId
+		b.SellerName = v.SellerName
+		b.Leader = v.Leader
+		b.IndustryId = v.IndustryId
+		b.IndustryName = v.IndustryName
+		b.CapitalScale = v.CapitalScale
+		b.ResearchTeamSize = v.ResearchTeamSize
+		b.UserMax = v.UserMax
+		b.SigningStatus = v.SigningStatus
+		b.Enable = v.Enable
+		b.ContractId = v.ContractId
+		b.SigningTime = utils.TimeTransferString(utils.FormatDate, v.SigningTime)
+		b.ExpiredTime = utils.TimeTransferString(utils.FormatDate, v.ExpiredTime)
+		b.CreateTime = v.CreateTime.Format(utils.FormatDateTime)
+		b.ModifyTime = v.ModifyTime.Format(utils.FormatDateTime)
+		b.Nation = v.Nation
+		items = append(items, b)
+	}
+
+	resp.List = items
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}

+ 764 - 0
controllers/eta_business/eta_business_menu.go

@@ -0,0 +1,764 @@
+package eta_business
+
+import (
+	"encoding/json"
+	"eta/eta_forum_admin/controllers"
+	"eta/eta_forum_admin/models"
+	"eta/eta_forum_admin/models/eta_business"
+	etaBusinessService "eta/eta_forum_admin/services/eta_business"
+	"eta/eta_forum_admin/utils"
+	"html/template"
+	"sort"
+	"strings"
+	"time"
+)
+
+// EtaBusinessMenuController ETA商家菜单
+type EtaBusinessMenuController struct {
+	controllers.BaseAuthController
+}
+
+// List
+// @Title 商家菜单列表
+// @Description 商家菜单列表
+// @Param   BusinessId	query	int		false	"商家ID"
+// @Param   Keyword		query	string	false	"关键词: 菜单名称"
+// @Success 200 Ret=200 获取成功
+// @router /menu/list [get]
+func (this *EtaBusinessMenuController) List() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	resp := new(eta_business.EtaBusinessMenuListResp)
+	resp.ChoiceList = make([]int, 0)
+	resp.HalfChoiceList = make([]int, 0)
+	resp.List = make([]*eta_business.EtaBusinessMenuItem, 0)
+	// 权限校验
+	ok, e := etaBusinessService.CheckEtaBusinessOperateAuth(sysUser.RoleTypeCode)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "操作权限校验失败, ErrMsg: " + e.Error()
+		return
+	}
+	if !ok {
+		br.Data = resp
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "获取成功"
+		return
+	}
+
+	menuCond := ``
+	menuPars := make([]interface{}, 0)
+	// 商家勾选的权限
+	{
+		businessId, _ := this.GetInt("EtaBusinessId", 0)
+		if businessId > 0 {
+			relateOb := new(eta_business.EtaBusinessMenuRelate)
+			cond := ` AND eta_business_id = ?`
+			pars := make([]interface{}, 0)
+			pars = append(pars, businessId)
+			relates, e := relateOb.GetItemsByCondition(cond, pars, []string{}, "")
+			if e != nil {
+				br.Msg = "获取失败"
+				br.ErrMsg = "获取商家菜单关联列表失败, Err: " + e.Error()
+				return
+			}
+			for _, r := range relates {
+				if r.Type == 1 {
+					resp.HalfChoiceList = append(resp.HalfChoiceList, r.MenuId)
+					continue
+				}
+				resp.ChoiceList = append(resp.ChoiceList, r.MenuId)
+			}
+			menuCond += ` AND hidden = 0`
+		}
+	}
+
+	// 关键词
+	keyword := this.GetString("Keyword")
+	keyword = strings.TrimSpace(keyword)
+	keyword = template.HTMLEscapeString(keyword)
+
+	// 所有菜单列表
+	menuOb := new(eta_business.EtaBusinessMenu)
+	order := `sort ASC, create_time DESC, menu_id DESC`
+	menus, e := menuOb.GetItemsByCondition(menuCond, menuPars, []string{}, order)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取商家菜单列表失败, Err: " + e.Error()
+		return
+	}
+
+	list := make([]*eta_business.EtaBusinessMenu, 0)
+	if keyword != "" {
+		menuMap := make(map[int]*eta_business.EtaBusinessMenu)
+		parentMap := make(map[int]*eta_business.EtaBusinessMenu)
+		existMap := make(map[int]bool)
+
+		for _, m := range menus {
+			menuMap[m.MenuId] = m
+		}
+		for _, m := range menus {
+			if m.ParentId > 0 {
+				parentMap[m.MenuId] = menuMap[m.ParentId]
+			}
+		}
+
+		// 遍历菜单, 取出跟关键词匹配的菜单(层级比较深, 递归效率很低, 产品要这个需求暂时也没啥办法优化=_=!)
+		for _, m := range menus {
+			if !strings.Contains(m.Name, keyword) {
+				continue
+			}
+			if existMap[m.MenuId] {
+				continue
+			}
+			existMap[m.MenuId] = true
+			list = append(list, m)
+
+			// 取出关键词所匹配的所有父级菜单
+			if m.ParentId > 0 {
+				parents := etaBusinessService.GetMenuParentsRecursive(menus, m.ParentId)
+				for _, p := range parents {
+					if !existMap[p.MenuId] {
+						existMap[p.MenuId] = true
+						list = append(list, p)
+					}
+				}
+			}
+		}
+
+		sort.Slice(list, func(i, j int) bool {
+			return list[j].Sort > list[i].Sort
+		})
+	} else {
+		list = menus
+	}
+
+	items := make([]*eta_business.EtaBusinessMenuItem, 0)
+	for _, v := range list {
+		t := &eta_business.EtaBusinessMenuItem{
+			MenuId:     v.MenuId,
+			ParentId:   v.ParentId,
+			Name:       v.Name,
+			Sort:       v.Sort,
+			Path:       v.Path,
+			IconPath:   v.IconPath,
+			Component:  v.Component,
+			Hidden:     v.Hidden,
+			MenuType:   v.MenuType,
+			ButtonCode: v.ButtonCode,
+			CreateTime: v.CreateTime.Format(utils.FormatDateTime),
+			Children:   make([]*eta_business.EtaBusinessMenuItem, 0),
+		}
+		items = append(items, t)
+	}
+
+	// 递归返回树形结构
+	items = etaBusinessService.GetMenuTreeRecursive(items, 0)
+
+	resp.List = items
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// SaveRelate
+// @Title 保存商家菜单关联
+// @Description 保存商家菜单关联
+// @Param	request	body eta_business.EtaBusinessAddReq true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /menu/relate/save [post]
+func (this *EtaBusinessMenuController) SaveRelate() {
+	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
+	}
+
+	// 操作权限校验
+	ok, e := etaBusinessService.CheckEtaBusinessOperateAuth(sysUser.RoleTypeCode)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "操作权限校验失败, ErrMsg: " + e.Error()
+		return
+	}
+	if !ok {
+		br.Msg = "无权操作"
+		return
+	}
+
+	// 参数校验
+	var req eta_business.EtaBusinessMenuRelateSaveReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	if req.EtaBusinessId <= 0 {
+		br.Msg = "参数有误"
+		return
+	}
+	if len(req.MenuIds) == 0 {
+		br.Msg = "请选择菜单"
+		return
+	}
+
+	ob := new(eta_business.EtaBusinessMenuRelate)
+	items := make([]*eta_business.EtaBusinessMenuRelate, 0)
+	for _, v := range req.MenuIds {
+		t := new(eta_business.EtaBusinessMenuRelate)
+		t.MenuId = v
+		t.EtaBusinessId = req.EtaBusinessId
+		items = append(items, t)
+	}
+	for _, v := range req.HalfMenuIds {
+		t := new(eta_business.EtaBusinessMenuRelate)
+		t.MenuId = v
+		t.Type = 1
+		t.EtaBusinessId = req.EtaBusinessId
+		items = append(items, t)
+	}
+	if e := ob.CreateMulti(req.EtaBusinessId, items); e != nil {
+		br.Msg = "保存失败"
+		br.ErrMsg = "保存商家菜单关联失败, Err: " + e.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// Add
+// @Title 新增菜单
+// @Description 新增菜单
+// @Param	request	body eta_business.EtaBusinessMenuAddReq true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /menu/add [post]
+func (this *EtaBusinessMenuController) Add() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	// 操作权限校验
+	ok, e := etaBusinessService.CheckEtaBusinessOperateAuth(sysUser.RoleTypeCode)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "操作权限校验失败, ErrMsg: " + e.Error()
+		return
+	}
+	if !ok {
+		br.Msg = "无权操作"
+		return
+	}
+
+	// 参数校验
+	var req eta_business.EtaBusinessMenuAddReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	req.Name = strings.TrimSpace(req.Name)
+	if req.Name == "" {
+		br.Msg = "请输入名称"
+		return
+	}
+	if req.MenuType == 0 {
+		req.Path = strings.TrimSpace(req.Path)
+		if req.Path == "" {
+			br.Msg = "请输入路由地址"
+			return
+		}
+		req.Component = strings.TrimSpace(req.Component)
+		if req.Component == "" {
+			br.Msg = "请输入组件路径"
+			return
+		}
+	} else {
+		req.ButtonCode = strings.TrimSpace(req.ButtonCode)
+		if req.ButtonCode == "" {
+			br.Msg = "请输入按钮/字段ID"
+			return
+		}
+	}
+
+	// 重名校验
+	{
+		ob := new(eta_business.EtaBusinessMenu)
+		cond := ` AND parent_id = ? AND name = ?`
+		pars := make([]interface{}, 0)
+		pars = append(pars, req.ParentId, req.Name)
+		exist, e := ob.GetItemByCondition(cond, pars)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			br.Msg = "操作失败"
+			br.ErrMsg = "获取同名菜单失败, Err: " + e.Error()
+			return
+		}
+		if exist != nil {
+			br.Msg = "名称已存在"
+			return
+		}
+	}
+
+	// 新增
+	menuOb := new(eta_business.EtaBusinessMenu)
+	menuOb.ParentId = req.ParentId
+	menuOb.Name = req.Name
+	menuOb.Sort = req.Sort
+	menuOb.Path = req.Path
+	menuOb.IconPath = req.IconPath
+	menuOb.Component = req.Component
+	menuOb.Hidden = req.Hidden
+	menuOb.MenuType = req.MenuType
+	menuOb.ButtonCode = req.ButtonCode
+	menuOb.CreateTime = time.Now().Local()
+	menuOb.ModifyTime = time.Now().Local()
+	// TODO:多级菜单
+	if menuOb.ParentId > 0 {
+		menuOb.LevelPath = menuOb.Component
+	}
+	if e := menuOb.Create(); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "新增菜单失败, Err: " + e.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// Edit
+// @Title 编辑菜单
+// @Description 编辑菜单
+// @Param	request	body eta_business.EtaBusinessMenuEditReq true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /menu/edit [post]
+func (this *EtaBusinessMenuController) Edit() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	// 操作权限校验
+	ok, e := etaBusinessService.CheckEtaBusinessOperateAuth(sysUser.RoleTypeCode)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "操作权限校验失败, ErrMsg: " + e.Error()
+		return
+	}
+	if !ok {
+		br.Msg = "无权操作"
+		return
+	}
+
+	// 参数校验
+	var req eta_business.EtaBusinessMenuEditReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	if req.MenuId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = "参数有误, MenuId"
+		return
+	}
+	req.Name = strings.TrimSpace(req.Name)
+	if req.Name == "" {
+		br.Msg = "请输入名称"
+		return
+	}
+	if req.MenuType == 0 {
+		req.Path = strings.TrimSpace(req.Path)
+		if req.Path == "" {
+			br.Msg = "请输入路由地址"
+			return
+		}
+		req.Component = strings.TrimSpace(req.Component)
+		if req.Component == "" {
+			br.Msg = "请输入组件路径"
+			return
+		}
+	} else {
+		req.ButtonCode = strings.TrimSpace(req.ButtonCode)
+		if req.ButtonCode == "" {
+			br.Msg = "请输入按钮/字段ID"
+			return
+		}
+	}
+
+	menuOb := new(eta_business.EtaBusinessMenu)
+	item, e := menuOb.GetItemById(req.MenuId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "菜单不存在, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取菜单失败, Err: " + e.Error()
+		return
+	}
+
+	// 重名校验
+	{
+		ob := new(eta_business.EtaBusinessMenu)
+		cond := ` AND parent_id = ? AND name = ?`
+		pars := make([]interface{}, 0)
+		pars = append(pars, req.ParentId, req.Name)
+		exist, e := ob.GetItemByCondition(cond, pars)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			br.Msg = "操作失败"
+			br.ErrMsg = "获取同名菜单失败, Err: " + e.Error()
+			return
+		}
+		if exist != nil && exist.MenuId != item.MenuId {
+			br.Msg = "名称已存在"
+			return
+		}
+	}
+
+	// 更新
+	item.Name = req.Name
+	item.Sort = req.Sort
+	item.Path = req.Path
+	item.IconPath = req.IconPath
+	item.Component = req.Component
+	item.Hidden = req.Hidden
+	item.ButtonCode = req.ButtonCode
+	item.ParentId = req.ParentId
+	item.ModifyTime = time.Now().Local()
+	// TODO:多级菜单
+	cols := make([]string, 0)
+	cols = append(cols, "Name", "Sort", "Path", "IconPath", "Component", "Hidden", "ButtonCode", "ParentId", "ModifyTime")
+	if item.ParentId > 0 {
+		item.LevelPath = item.Component
+		cols = append(cols, "LevelPath")
+	}
+	if e := item.Update(cols); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "更新菜单失败, Err: " + e.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// Remove
+// @Title 删除菜单
+// @Description 删除菜单
+// @Param	request	body eta_business.EtaBusinessMenuRemoveReq true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /menu/remove [post]
+func (this *EtaBusinessMenuController) Remove() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	// 操作权限校验
+	ok, e := etaBusinessService.CheckEtaBusinessOperateAuth(sysUser.RoleTypeCode)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "操作权限校验失败, ErrMsg: " + e.Error()
+		return
+	}
+	if !ok {
+		br.Msg = "无权操作"
+		return
+	}
+
+	// 参数校验
+	var req eta_business.EtaBusinessMenuRemoveReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	if req.MenuId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = "参数有误, MenuId"
+		return
+	}
+
+	menuOb := new(eta_business.EtaBusinessMenu)
+	_, e = menuOb.GetItemById(req.MenuId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "菜单不存在, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取菜单失败, Err: " + e.Error()
+		return
+	}
+
+	// 获取所有菜单列表, 筛选出该菜单及子菜单
+	menus, e := menuOb.GetItemsByCondition(``, make([]interface{}, 0), []string{}, "")
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取菜单列表失败, Err: " + e.Error()
+		return
+	}
+	menuIds := etaBusinessService.GetMenuChildrenIdsRecursive(menus, req.MenuId)
+	menuIds = append(menuIds, req.MenuId)
+
+	// 删除菜单
+	if e = menuOb.MultiDel(menuIds); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "删除菜单及子菜单失败, Err: " + e.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// IconList
+// @Title 图标列表
+// @Description 图标列表
+// @Success 200 Ret=200 操作成功
+// @router /menu/icon/list [get]
+func (this *EtaBusinessMenuController) IconList() {
+	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
+	}
+
+	iconOb := new(eta_business.EtaBusinessMenuIcon)
+	icons, e := iconOb.GetItemsByCondition(``, make([]interface{}, 0), []string{}, "")
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取菜单列表失败, Err: " + e.Error()
+		return
+	}
+	list := make([]*eta_business.EtaBusinessMenuIconItem, 0)
+	for _, v := range icons {
+		list = append(list, &eta_business.EtaBusinessMenuIconItem{
+			IconId:   v.IconId,
+			IconPath: v.IconPath,
+		})
+	}
+
+	br.Data = list
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// IconAdd
+// @Title 新增图标
+// @Description 新增图标
+// @Success 200 Ret=200 操作成功
+// @router /menu/icon/add [post]
+func (this *EtaBusinessMenuController) IconAdd() {
+	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 eta_business.EtaBusinessMenuIconAddReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	req.IconPath = strings.TrimSpace(req.IconPath)
+	if req.IconPath == "" {
+		br.Msg = "请上传图片"
+		return
+	}
+
+	iconOb := new(eta_business.EtaBusinessMenuIcon)
+	iconOb.IconPath = req.IconPath
+	iconOb.CreateTime = time.Now().Local()
+	if e := iconOb.Create(); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "新增菜单icon失败, Err: " + e.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// SystemApiConfig
+// @Title 系统配置接口列表
+// @Description 系统配置接口列表
+// @Success 200 {object} []system.BusinessConf
+// @router /api/config/list [get]
+func (this *EtaBusinessMenuController) SystemApiConfig() {
+	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 relates []*eta_business.EtaBusinessConfigRelate
+	businessId, _ := this.GetInt("EtaBusinessId", 0)
+	var err error
+	if businessId > 0 {
+		relateOb := new(eta_business.EtaBusinessConfigRelate)
+		cond := ` AND eta_business_id = ?`
+		pars := make([]interface{}, 0)
+		pars = append(pars, businessId)
+		relates, err = relateOb.GetItemsByCondition(cond, pars, []string{}, "")
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取商家菜单关联列表失败, Err: " + err.Error()
+			return
+		}
+	}
+
+	br.Data = relates
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// SystemApiConfig
+// @Title 系统配置接口列表
+// @Description 系统配置接口列表
+// @Success 200 {object} []system.BusinessConf
+// @router /api/config/set [post]
+func (this *EtaBusinessMenuController) SystemApiConfigSet() {
+	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 eta_business.SetBusinessConfReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	ob := new(eta_business.EtaBusinessConfigRelate)
+	items := make([]*eta_business.EtaBusinessConfigRelate, 0)
+	for _, v := range req.List {
+		t := new(eta_business.EtaBusinessConfigRelate)
+		t.ConfigCode = v.ConfigCode
+		t.ConfigValue = v.ConfigValue
+		t.EtaBusinessId = req.EtaBusinessId
+		items = append(items, t)
+	}
+	if e := ob.CreateMulti(req.EtaBusinessId, items); e != nil {
+		br.Msg = "保存失败"
+		br.ErrMsg = "保存商家菜单关联失败, Err: " + e.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "设置成功"
+}

+ 1369 - 0
controllers/eta_business/user.go

@@ -0,0 +1,1369 @@
+package eta_business
+
+import (
+	"encoding/json"
+	"eta/eta_forum_admin/controllers"
+	"eta/eta_forum_admin/models"
+	"eta/eta_forum_admin/models/eta_business"
+	"eta/eta_forum_admin/models/system"
+	eta_business2 "eta/eta_forum_admin/services/eta_business"
+	"eta/eta_forum_admin/utils"
+	"fmt"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"github.com/tealeg/xlsx"
+	"os"
+	"strings"
+	"time"
+)
+
+// 客户联系人管理
+type EtaBusinessUserController struct {
+	controllers.BaseAuthController
+}
+
+// List
+// @Title 联系人列表
+// @Description 联系人列表接口
+// @Param   PageSize   query   int  true       "每页数据条数"
+// @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
+// @Param   CompanyId   query   int  true       "公司id,必填"
+// @Param   KeyWord   query   string  true       "搜索关键词"
+// @Param   ProductType   query   int  true       "产品类型"
+// @Param   ProductId   query   int  true       "产品id"
+// @Param   IsSubscribe   query   int  true       "是否关注了公众号: -1-默认全部; 0-未关注; 1-已关注"
+// @Param   IsSubscribeHzyj   query   int  true       "是否关注了弘则研究公众号: -1-默认全部; 0-未关注; 1-已关注"
+// @Param   IsSubscribeCygx   query   int  true       "是否关注了查研观向小助手公众号: -1-默认全部; 0-未关注; 1-已关注"
+// @Success 200 {object} company.CompanyUserListResp
+// @router /user/list [get]
+func (this *EtaBusinessUserController) List() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		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")
+	positionStatus, _ := this.GetInt("PositionStatus", -1)
+	enabled, _ := this.GetInt("Enabled", -1)
+	businessCode := this.GetString("BusinessCode")
+	etaBusinessId, _ := this.GetInt("EtaBusinessId")
+	keyword := utils.TrimStr(this.GetString("Keyword"))
+
+	sortType := this.GetString("SortType")
+	sortParam := this.GetString("SortParam")
+
+	/*if businessCode <= 0 {
+		br.Msg = "请选择客户"
+		br.ErrMsg = "客户参数错误"
+		return
+	}*/
+	var startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = paging.StartIndex(currentIndex, pageSize)
+
+	var condition string
+	var pars []interface{}
+
+	if keyword != "" {
+		condition += ` AND (real_name LIKE '%` + keyword + `%' OR mobile LIKE '%` + keyword + `%' OR email LIKE '%` + keyword + `%') `
+	}
+
+	if businessCode != "" {
+		condition += ` AND business_code = ? `
+		pars = append(pars, businessCode)
+	}
+
+	if etaBusinessId != 0 {
+		condition += ` AND eta_business_id = ? `
+		pars = append(pars, etaBusinessId)
+	}
+
+	if positionStatus >= 0 {
+		condition += ` AND position_status = ? `
+		pars = append(pars, positionStatus)
+	}
+
+	if enabled >= 0 { // 0-禁用 1-启用
+		condition += ` AND enabled = ? `
+		pars = append(pars, enabled)
+	}
+	order := ""
+	if sortParam == "LastLoginTime" {
+		if sortType == "desc" || sortType == "asc" {
+			order = " last_login_time " + sortType
+		}
+	}
+	total, err := models.GetUserCountByCondition(condition, pars)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据总数失败,Err:" + err.Error()
+		return
+	}
+
+	list, err := models.GetUserPageListByCondition(condition, pars, order, startSize, pageSize)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+	page := paging.GetPaging(currentIndex, pageSize, total)
+	resp := new(models.UserListResp)
+	userList := make([]*models.BusinessUser, 0)
+	if len(list) == 0 {
+		resp.List = userList
+		resp.Paging = page
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "获取成功"
+		br.Data = resp
+		return
+	}
+	// 查找商户信息
+	businessCodeStr := make([]string, 0)
+	businessCodeMap := make(map[string]*eta_business.EtaBusiness)
+	for _, v := range list {
+		if _, ok := businessCodeMap[v.BusinessCode]; ok {
+			continue
+		}
+		businessCodeStr = append(businessCodeStr, v.BusinessCode)
+	}
+	obj := new(eta_business.EtaBusiness)
+	condition1 := " AND business_code IN (" + utils.GetOrmInReplace(len(businessCodeStr)) + ") "
+	var pars1 []interface{}
+	pars1 = append(pars1, businessCodeStr)
+	businessList, err := obj.GetItemsByCondition(condition1, pars1, []string{}, "")
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取商户信息失败,Err:" + err.Error()
+		return
+	}
+	for _, v := range businessList {
+		businessCodeMap[v.BusinessCode] = v
+	}
+
+	for _, v := range list {
+		tmp := new(models.BusinessUser)
+		if businessInfo, ok := businessCodeMap[v.BusinessCode]; ok {
+			tmp.BusinessName = businessInfo.BusinessName
+			tmp.BusinessSellerId = businessInfo.SellerId
+			tmp.BusinessSellerName = businessInfo.SellerName
+		}
+		tmp.BusinessCode = v.BusinessCode
+		tmp.EtaBusinessId = v.EtaBusinessId
+		tmp.UserId = v.UserId
+		tmp.RealName = v.RealName
+		tmp.Mobile = v.Mobile
+		tmp.CountryCode = v.CountryCode
+		tmp.Email = v.Email
+		tmp.Enabled = v.Enabled
+		tmp.PositionStatus = v.PositionStatus
+		tmp.Position = v.Position
+		tmp.DepartmentName = v.DepartmentName
+		// 判断time类型是否为零值
+		if !v.LastLoginTime.IsZero() {
+			tmp.LastLoginTime = v.LastLoginTime.Format(utils.FormatDateTime)
+		}
+		tmp.CreatedTime = v.CreatedTime.Format(utils.FormatDateTime)
+		tmp.LastUpdatedTime = v.LastUpdatedTime.Format(utils.FormatDateTime)
+		userList = append(userList, tmp)
+	}
+	resp.List = userList
+	resp.Paging = page
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// @Title 新增客户联系人
+// @Description 新增客户联系人接口
+// @Param	request	body company.AddUserReq true "type json string"
+// @router /user/add [post]
+func (this *EtaBusinessUserController) AddUser() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		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.AddUserReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.RealName == "" {
+		br.Msg = "请填写姓名"
+		br.IsSendEmail = false
+		return
+	}
+
+	if req.Mobile == "" {
+		br.Msg = "请输入手机号"
+		br.IsSendEmail = false
+		return
+	}
+
+	if req.EtaBusinessId == 0 {
+		br.Msg = "请选择客户"
+		br.IsSendEmail = false
+		return
+	}
+
+	if req.CountryCode == "" {
+		req.CountryCode = "86"
+	}
+
+	//空格移除
+	req.Mobile = utils.TrimStr(req.Mobile)
+	req.RealName = utils.TrimStr(req.RealName)
+	//判断该手机号、邮箱是否已经添加,如果已经添加,那么就不再添加
+	var key string
+	if req.Mobile != "" {
+		key = fmt.Sprintf("user:mobile:%s_%s", req.CountryCode, req.Mobile)
+	}
+	isHas := utils.Rc.IsExist(key)
+	if isHas == true {
+		br.Msg = "重复添加"
+		return
+	} else {
+		//设置3分钟缓存,不允许重复添加
+		utils.Rc.SetNX(key, 1, time.Second*300)
+
+		//添加完成删除对应的缓存
+		defer utils.Rc.Delete(key)
+	}
+
+	businessObj := new(eta_business.EtaBusiness)
+
+	businessInfo, err := businessObj.GetItemById(req.EtaBusinessId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			br.Msg = "商户不存在"
+			return
+		}
+		br.Msg = "客户信息有误"
+		br.ErrMsg = "获取客户信息失败, Err: " + err.Error()
+		return
+	}
+	//操作权限校验
+	ok := eta_business2.CheckBusinessUserButton(sysUser.RoleTypeCode, businessInfo.SellerId, sysUser.AdminId)
+	if !ok {
+		br.Msg = "没有操作权限"
+		br.ErrMsg = "没有操作权限"
+		return
+	}
+	//systemUser models.Company.CompanyUser
+	//var sysemUser company.CompanyUser
+	//校验主手机号
+	mobileItem, err := models.GetUserByMobileCountryCode(req.Mobile, req.CountryCode)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "校验手机号有误"
+		br.ErrMsg = "校验手机号有误, Err: " + err.Error()
+		return
+	}
+	if err == nil && mobileItem.UserId > 0 {
+		br.Msg = "手机号已存在"
+		br.Success = true
+		br.Data = mobileItem
+		br.IsSendEmail = false
+		br.Ret = 600
+		return
+	}
+	err = nil
+
+	user := new(models.User)
+	user.RealName = utils.TrimStr(req.RealName)
+	user.CountryCode = utils.TrimStr(req.CountryCode)
+	user.Mobile = utils.TrimStr(req.Mobile)
+	user.UserName = user.Mobile
+	user.Position = req.Position
+	user.PositionStatus = req.PositionStatus
+	user.Enabled = 1
+	user.BusinessCode = businessInfo.BusinessCode
+	user.EtaBusinessId = businessInfo.EtaBusinessId
+	user.DepartmentName = req.DepartmentName
+	user.CreatedTime = time.Now()
+	user.RegisterTime = time.Now()
+	user.LastUpdatedTime = time.Now()
+	user.SellerId = sysUser.AdminId
+	user.SellerName = sysUser.RealName
+	newId, err := models.AddUser(user)
+	user.UserId = int(newId)
+	if err != nil {
+		br.Msg = "新增失败"
+		br.ErrMsg = "新增失败,Err:" + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "新增成功"
+	br.Data = newId
+	br.IsAddLog = true
+}
+
+// @Title 删除客户联系人
+// @Description 删除客户联系人接口
+// @Param	request	body company.DeleteUserReq true "type json string"
+// @router /user/delete [post]
+func (this *EtaBusinessUserController) DeleteUser() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		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.DeleteUserReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	if req.UserId <= 0 {
+		br.Msg = "参数错误!"
+		return
+	}
+
+	//获取联系人详情
+	userInfo, err := models.GetUserByUserId(req.UserId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			br.Msg = "用户不存在"
+			return
+		}
+		br.Msg = "获取联系人异常!"
+		br.ErrMsg = "获取联系人异常,Err:" + err.Error()
+		return
+	}
+	// 查询商户关联的销售信息
+	businessObj := new(eta_business.EtaBusiness)
+
+	businessInfo, err := businessObj.GetItemByBusinessCode(userInfo.BusinessCode)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			br.Msg = "商户不存在"
+			return
+		}
+		br.Msg = "客户信息有误"
+		br.ErrMsg = "获取客户信息失败, Err: " + err.Error()
+		return
+	}
+	//操作权限校验
+	ok := eta_business2.CheckBusinessUserButton(sysUser.RoleTypeCode, businessInfo.SellerId, sysUser.AdminId)
+	if !ok {
+		br.Msg = "删除失败,没有权限"
+		br.ErrMsg = "删除失败,没有权限"
+		return
+	}
+
+	err = models.DeleteUser(req.UserId)
+	if err != nil {
+		br.Msg = "删除失败"
+		br.ErrMsg = "删除失败,Err:" + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "删除成功"
+	br.IsAddLog = true
+}
+
+// @Title 编辑客户联系人
+// @Description 编辑客户联系人接口
+// @Param	request	body company.EditUserReq true "type json string"
+// @router /user/edit [post]
+func (this *EtaBusinessUserController) EditUser() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		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.EditUserReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	if req.UserId <= 0 {
+		br.Msg = "参数错误!"
+		return
+	}
+
+	//移除空格
+	req.Mobile = utils.TrimStr(req.Mobile)
+	req.RealName = utils.TrimStr(req.RealName)
+
+	if req.RealName == "" {
+		br.Msg = "请填写姓名"
+		br.IsSendEmail = false
+		return
+	}
+
+	if req.Mobile == "" {
+		br.Msg = "请输入手机号"
+		br.IsSendEmail = false
+		return
+	}
+
+	if req.EtaBusinessId == 0 {
+		br.Msg = "请选择客户"
+		br.IsSendEmail = false
+		return
+	}
+
+	if req.CountryCode == "" {
+		req.CountryCode = "86"
+	}
+
+	//操作权限校验
+	userInfo, err := models.GetUserByUserId(req.UserId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			br.Msg = "用户不存在"
+			return
+		}
+		br.ErrMsg = "获取信息失败,Err:" + err.Error()
+		br.Msg = "获取信息失败"
+		return
+	}
+
+	businessObj := new(eta_business.EtaBusiness)
+
+	businessInfo, err := businessObj.GetItemById(req.EtaBusinessId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			br.Msg = "商户不存在"
+			return
+		}
+		br.Msg = "客户信息有误"
+		br.ErrMsg = "获取客户信息失败, Err: " + err.Error()
+		return
+	}
+	//操作权限校验
+	ok := eta_business2.CheckBusinessUserButton(sysUser.RoleTypeCode, businessInfo.SellerId, sysUser.AdminId)
+	if !ok {
+		br.Msg = "没有操作权限"
+		br.ErrMsg = "没有操作权限"
+		return
+	}
+
+	if req.Mobile != "" && req.Mobile != userInfo.Mobile && req.CountryCode != userInfo.CountryCode {
+		mobileItem, e := models.GetUserByMobileCountryCode(req.Mobile, req.CountryCode)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			br.Msg = "校验手机号有误"
+			br.ErrMsg = "校验手机号有误, Err: " + err.Error()
+			return
+		}
+		if e == nil && mobileItem.UserId > 0 && mobileItem.UserId != req.UserId {
+			{
+				br.Msg = "手机号已存在"
+				br.Success = true
+				br.Data = mobileItem
+				br.IsSendEmail = false
+				br.Ret = 600
+				return
+			}
+		}
+	}
+
+	//待更新字段
+	updateCol := []string{"RealName", "Mobile", "UserName", "LastUpdatedTime", "Position", "PositionStatus", "DepartmentName", "BusinessCode", "EtaBusinessId", "BusinessCode", "CountryCode"}
+
+	userInfo.RealName = req.RealName
+	userInfo.Mobile = req.Mobile
+	userInfo.UserName = userInfo.Mobile
+	userInfo.LastUpdatedTime = time.Now()
+	userInfo.Position = req.Position
+	userInfo.DepartmentName = req.DepartmentName
+	userInfo.BusinessCode = businessInfo.BusinessCode
+	userInfo.EtaBusinessId = businessInfo.EtaBusinessId
+	userInfo.CountryCode = req.CountryCode
+	userInfo.PositionStatus = req.PositionStatus
+	err = userInfo.Update(updateCol)
+	if err != nil {
+		br.Msg = "编辑失败!"
+		br.ErrMsg = "编辑失败,Err:" + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "编辑成功"
+	br.IsAddLog = true
+}
+
+// @Title 获取批量导入联系人数据
+// @Description 获取批量导入联系人数据
+// @Param   File   query   file  true       "文件"
+// @Param   CompanyId   query   file  true       "客户id"
+// @Success 200 {object} models.ImportListResp
+// @router /user/import/list [post]
+func (this *EtaBusinessUserController) ImportList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请重新登录"
+		return
+	}
+	file, h, err := this.GetFile("File")
+	if err != nil {
+		br.Msg = "获取文件失败"
+		br.ErrMsg = "获取文件失败,Err:" + err.Error()
+		return
+	}
+
+	//todo 没有选择对应的商户所以无操作权限校验
+
+	uploadDir := "static/xls"
+	err = os.MkdirAll(uploadDir, 766)
+	if err != nil {
+		br.Msg = "存储目录创建失败"
+		br.ErrMsg = "存储目录创建失败,Err:" + err.Error()
+		return
+	}
+	path := uploadDir + "/" + time.Now().Format(utils.FormatDateTimeUnSpace) + h.Filename
+	defer file.Close()
+	err = this.SaveToFile("File", path)
+	if err != nil {
+		br.Msg = "文件保存失败"
+		br.ErrMsg = "文件保存失败,Err:" + err.Error()
+		return
+	}
+	xlFile, err := xlsx.OpenFile(path)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "打开文件失败,Err:" + err.Error() + ";path:" + path
+		return
+	}
+	existUser := make([]*models.BusinessUser, 0)
+	addUser := make([]*models.BusinessUser, 0)
+	//excel中已经存在的数据,用来判断excel中是否存在相同手机号/邮箱,避免重复提交
+	excelData := make(map[string]string)
+	//重复数据
+	repeatUser := make([]*models.BusinessUser, 0)
+
+	// 遍历sheet页读取
+	for _, sheet := range xlFile.Sheets {
+		//遍历行读取
+		maxRow := sheet.MaxRow
+		fmt.Println("maxRow:", maxRow)
+		fmt.Println("maxRow")
+		for i := 0; i < maxRow; i++ {
+			if i == 1 {
+				row := sheet.Row(i)
+				cells := row.Cells
+				for k, cell := range cells {
+					text := cell.String()
+					if k == 0 {
+						if text != "姓名" {
+							br.Msg = "模板格式错误"
+							br.ErrMsg = "模板格式错误,title:" + text
+							return
+						}
+					}
+					if k == 1 {
+						if !strings.Contains(text, "区号") {
+							br.Msg = "模板格式错误"
+							br.ErrMsg = "模板格式错误,title:" + text
+							return
+						}
+					}
+					if k == 2 {
+						if !strings.Contains(text, "手机号") {
+							br.Msg = "模板格式错误"
+							br.ErrMsg = "模板格式错误,title:" + text
+							return
+						}
+					}
+					if k == 3 {
+						if !strings.Contains(text, "岗位") {
+							br.Msg = "模板格式错误"
+							br.ErrMsg = "模板格式错误,title:" + text
+							return
+						}
+					}
+					if k == 4 {
+						if !strings.Contains(text, "部门") {
+							br.Msg = "模板格式错误"
+							br.ErrMsg = "模板格式错误,title:" + text
+							return
+						}
+					}
+					if k == 5 {
+						if !strings.Contains(text, "客户名称") {
+							br.Msg = "模板格式错误"
+							br.ErrMsg = "模板格式错误,title:" + text
+							return
+						}
+					}
+					if k == 6 {
+						if !strings.Contains(text, "社会信用码") {
+							br.Msg = "模板格式错误"
+							br.ErrMsg = "模板格式错误,title:" + text
+							return
+						}
+					}
+					if k == 7 {
+						if !strings.Contains(text, "在职状态") {
+							br.Msg = "模板格式错误"
+							br.ErrMsg = "模板格式错误,title:" + text
+							return
+						}
+					}
+				}
+			}
+			if i >= 2 {
+				row := sheet.Row(i)
+				cells := row.Cells
+				var userName, countryCode, mobileOne, position, departmentName, businessName, creditCode, positionStatus string
+				for k, cell := range cells {
+					if k == 0 {
+						userName = cell.String()
+					}
+					if k == 1 {
+						countryCode = cell.String()
+					}
+					if k == 2 {
+						mobileOne = cell.String()
+					}
+					if k == 3 {
+						position = cell.String()
+					}
+					if k == 4 {
+						departmentName = cell.String()
+					}
+					if k == 5 {
+						businessName = cell.String()
+					}
+					if k == 6 {
+						creditCode = cell.String()
+					}
+					if k == 7 {
+						positionStatus = cell.String()
+					}
+				}
+
+				//移除空格
+				mobileOne = utils.TrimStr(mobileOne)
+				//这些字段都没有的话,系统认为excel到底了
+				if userName == "" {
+					utils.FileLog.Info("姓名必填")
+					continue
+				}
+
+				//如果手机号或者邮箱都没有填写的情况下
+				if mobileOne == "" {
+					utils.FileLog.Info("手机号必填")
+					continue
+				}
+				if countryCode == "" {
+					countryCode = "86"
+				}
+
+				if businessName == "" && creditCode == "" {
+					utils.FileLog.Info("导入数据中存在【姓名】和【社会信用码】都为空的用户,请检查")
+					continue
+				}
+
+				if positionStatus != "在职" && positionStatus != "离职" {
+					utils.FileLog.Info("导入数据中存在【在职状态】为空或不在【在职】和【离职】的用户,请检查")
+					continue
+				}
+
+				item := new(models.BusinessUser)
+				item.CountryCode = countryCode
+				item.RealName = userName
+				if positionStatus == "在职" {
+					item.PositionStatus = 1
+				} else {
+					item.PositionStatus = 0
+				}
+				item.Mobile = mobileOne
+				item.UserName = item.Mobile
+				item.Position = position
+				item.DepartmentName = departmentName
+				// 查询商户信息
+				if creditCode != "" {
+					businessObj := new(eta_business.EtaBusiness)
+					businessInfo := new(eta_business.EtaBusiness)
+					businessInfo, err = businessObj.GetItemByCreditCode(creditCode)
+					if err != nil {
+						if err.Error() == utils.ErrNoRow() {
+							utils.FileLog.Info("商户不存在")
+							continue
+						}
+						utils.FileLog.Info("获取商户信息失败, Err: " + err.Error())
+						continue
+					}
+					item.BusinessCode = businessInfo.BusinessCode
+					item.BusinessName = businessInfo.BusinessName
+				}
+				if item.BusinessCode == "" && businessName != "" {
+					businessObj := new(eta_business.EtaBusiness)
+					businessInfo := new(eta_business.EtaBusiness)
+					businessInfo, err = businessObj.GetItemByBusinessName(businessName)
+					if err != nil {
+						if err.Error() == utils.ErrNoRow() {
+							utils.FileLog.Info("商户不存在")
+							continue
+						}
+						utils.FileLog.Info("获取商户信息失败, Err: " + err.Error())
+						continue
+					}
+					item.BusinessCode = businessInfo.BusinessCode
+					item.BusinessName = businessInfo.BusinessName
+				}
+				if _, ok := excelData[item.CountryCode+"_"+item.Mobile]; ok {
+					//将用户插入其中,然后退出当前循环,进入下一循环
+					//repeatUser = append(repeatUser, item)
+					continue
+				}
+				excelData[item.CountryCode+"_"+item.Mobile] = item.CountryCode
+
+				//没问题数据
+				existUser = append(existUser, item)
+			}
+		}
+	}
+
+	// 判断系统里该手机号是否已存在
+	mobiles := make([]string, 0)
+	for k, _ := range excelData {
+		mobile := strings.Split(k, "_")[1]
+		mobiles = append(mobiles, mobile)
+	}
+
+	// 查询系统里是否存在
+	existUserMap := make(map[string]struct{})
+	if len(mobiles) > 0 {
+		existUsers, e := models.GetUserByMobiles(mobiles)
+		if e != nil {
+			br.Msg = "查询用户失败"
+			br.ErrMsg = "查询用户失败,Err:" + e.Error()
+			return
+		}
+		for _, user := range existUsers {
+			existUserMap[user.CountryCode+"_"+user.Mobile] = struct{}{}
+		}
+	}
+	for _, v := range existUser {
+		if _, ok := existUserMap[v.CountryCode+"_"+v.Mobile]; ok {
+			repeatUser = append(repeatUser, v)
+		} else {
+			addUser = append(addUser, v)
+		}
+	}
+	//defer func() {
+	//	os.Remove(path)
+	//}()
+	br.Msg = "获取成功"
+	br.Ret = 200
+	br.Success = true
+	br.Data = models.ImportListResp{
+		ValidUser:  addUser,
+		RepeatUser: repeatUser,
+	}
+}
+
+// @Title 批量导入联系人数据
+// @Description 批量导入联系人数据
+// @Param   File   query   file  true       "文件"
+// @Param   CompanyId   query   file  true       "客户id"
+// @Success 600 {object} []*company.CompanyUser
+// @Success 200 Ret=200 导入成功
+// @router /user/import [post]
+func (this *EtaBusinessUserController) Import() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请重新登录"
+		return
+	}
+	var req models.ImportUserReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	/*file, h, err := this.GetFile("File")
+	if err != nil {
+		br.Msg = "获取文件失败"
+		br.ErrMsg = "获取文件失败,Err:" + err.Error()
+		return
+	}
+
+	//todo 没有选择对应的商户所以无操作权限校验
+
+	uploadDir := "static/xls"
+	err = os.MkdirAll(uploadDir, 766)
+	if err != nil {
+		br.Msg = "存储目录创建失败"
+		br.ErrMsg = "存储目录创建失败,Err:" + err.Error()
+		return
+	}
+	path := uploadDir + "/" + time.Now().Format(utils.FormatDateTimeUnSpace) + h.Filename
+	defer file.Close()
+	err = this.SaveToFile("File", path)
+	if err != nil {
+		br.Msg = "文件保存失败"
+		br.ErrMsg = "文件保存失败,Err:" + err.Error()
+		return
+	}
+	xlFile, err := xlsx.OpenFile(path)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "打开文件失败,Err:" + err.Error() + ";path:" + path
+		return
+	}
+	existUser := make([]*models.User, 0)
+	addUser := make([]*models.User, 0)
+	//excel中已经存在的数据,用来判断excel中是否存在相同手机号/邮箱,避免重复提交
+	excelData := make(map[string]string)
+
+	// 遍历sheet页读取
+	for _, sheet := range xlFile.Sheets {
+		//遍历行读取
+		maxRow := sheet.MaxRow
+		fmt.Println("maxRow:", maxRow)
+		fmt.Println("maxRow")
+		for i := 0; i < maxRow; i++ {
+			if i == 1 {
+				row := sheet.Row(i)
+				cells := row.Cells
+				for k, cell := range cells {
+					text := cell.String()
+					if k == 0 {
+						if text != "姓名" {
+							br.Msg = "模板格式错误"
+							br.ErrMsg = "模板格式错误,title:" + text
+							return
+						}
+					}
+					if k == 1 {
+						if !strings.Contains(text, "区号") {
+							br.Msg = "模板格式错误"
+							br.ErrMsg = "模板格式错误,title:" + text
+							return
+						}
+					}
+					if k == 2 {
+						if !strings.Contains(text, "手机号") {
+							br.Msg = "模板格式错误"
+							br.ErrMsg = "模板格式错误,title:" + text
+							return
+						}
+					}
+					if k == 3 {
+						if !strings.Contains(text, "岗位") {
+							br.Msg = "模板格式错误"
+							br.ErrMsg = "模板格式错误,title:" + text
+							return
+						}
+					}
+					if k == 4 {
+						if !strings.Contains(text, "部门") {
+							br.Msg = "模板格式错误"
+							br.ErrMsg = "模板格式错误,title:" + text
+							return
+						}
+					}
+					if k == 5 {
+						if !strings.Contains(text, "客户名称") {
+							br.Msg = "模板格式错误"
+							br.ErrMsg = "模板格式错误,title:" + text
+							return
+						}
+					}
+					if k == 6 {
+						if !strings.Contains(text, "社会信用码") {
+							br.Msg = "模板格式错误"
+							br.ErrMsg = "模板格式错误,title:" + text
+							return
+						}
+					}
+					if k == 7 {
+						if !strings.Contains(text, "在职状态") {
+							br.Msg = "模板格式错误"
+							br.ErrMsg = "模板格式错误,title:" + text
+							return
+						}
+					}
+				}
+			}
+			if i >= 2 {
+				row := sheet.Row(i)
+				cells := row.Cells
+				var userName, countryCode, mobileOne, position, departmentName, businessName, creditCode, positionStatus string
+				for k, cell := range cells {
+					if k == 0 {
+						userName = cell.String()
+					}
+					if k == 1 {
+						countryCode = cell.String()
+					}
+					if k == 2 {
+						mobileOne = cell.String()
+					}
+					if k == 3 {
+						position = cell.String()
+					}
+					if k == 4 {
+						departmentName = cell.String()
+					}
+					if k == 5 {
+						businessName = cell.String()
+					}
+					if k == 6 {
+						creditCode = cell.String()
+					}
+					if k == 7 {
+						positionStatus = cell.String()
+					}
+				}
+
+				//移除空格
+				mobileOne = utils.TrimStr(mobileOne)
+				//这些字段都没有的话,系统认为excel到底了
+				if userName == "" {
+					utils.FileLog.Info("姓名必填")
+					continue
+				}
+
+				//如果手机号或者邮箱都没有填写的情况下
+				if mobileOne == "" {
+					utils.FileLog.Info("手机号必填")
+					continue
+				}
+				if countryCode == "" {
+					utils.FileLog.Info("导入数据中存在【国际区号】为空的用户,请检查")
+					continue
+				}
+
+				if businessName == "" && creditCode == "" {
+					utils.FileLog.Info("导入数据中存在【姓名】和【社会信用码】都为空的用户,请检查")
+					continue
+				}
+
+				item := new(models.User)
+				item.CountryCode = countryCode
+				item.RealName = userName
+				if positionStatus == "在职" {
+					item.PositionStatus = 1
+				} else if positionStatus == "离职" {
+					item.PositionStatus = 0
+				}
+				item.Mobile = mobileOne
+				item.UserName = item.Mobile
+				item.Position = position
+				item.DepartmentName = departmentName
+				// 查询商户信息
+				if creditCode != "" {
+					businessObj := new(eta_business.EtaBusiness)
+					businessInfo := new(eta_business.EtaBusiness)
+					businessInfo, err = businessObj.GetItemByCreditCode(creditCode)
+					if err != nil {
+						if err.Error() == utils.ErrNoRow() {
+							utils.FileLog.Info("商户不存在")
+							continue
+						}
+						utils.FileLog.Info("获取商户信息失败, Err: " + err.Error())
+						continue
+					}
+					item.BusinessCode = businessInfo.BusinessCode
+					item.EtaBusinessId = businessInfo.EtaBusinessId
+				}
+				if item.BusinessCode == "" && businessName != "" {
+					businessObj := new(eta_business.EtaBusiness)
+					businessInfo := new(eta_business.EtaBusiness)
+					businessInfo, err = businessObj.GetItemByBusinessName(businessName)
+					if err != nil {
+						if err.Error() == utils.ErrNoRow() {
+							utils.FileLog.Info("商户不存在")
+							continue
+						}
+						utils.FileLog.Info("获取商户信息失败, Err: " + err.Error())
+						continue
+					}
+					item.BusinessCode = businessInfo.BusinessCode
+					item.EtaBusinessId = businessInfo.EtaBusinessId
+				}
+				if _, ok := excelData[item.CountryCode+"_"+item.Mobile]; ok {
+					//将用户插入其中,然后退出当前循环,进入下一循环
+					//repeatUser = append(repeatUser, item)
+					continue
+				}
+				excelData[item.CountryCode+"_"+item.Mobile] = item.CountryCode
+
+				//没问题数据
+				existUser = append(existUser, item)
+			}
+		}
+	}
+	*/
+	excelUser := make([]*models.User, 0)
+	addUser := make([]*models.User, 0)
+	//excel中已经存在的数据,用来判断excel中是否存在相同手机号/邮箱,避免重复提交
+	excelData := make(map[string]string)
+	for _, v := range req.ValidUser {
+		// 查询商户信息
+		item := new(models.User)
+		if v.CountryCode == "" {
+			v.CountryCode = "86"
+		}
+		item.CountryCode = v.CountryCode
+		item.RealName = v.RealName
+		item.PositionStatus = v.PositionStatus
+		item.Mobile = v.Mobile
+		item.UserName = v.Mobile
+		item.Position = v.Position
+		item.DepartmentName = v.DepartmentName
+		if v.BusinessCode != "" {
+			businessObj := new(eta_business.EtaBusiness)
+			businessInfo := new(eta_business.EtaBusiness)
+			businessInfo, err = businessObj.GetItemByBusinessCode(v.BusinessCode)
+			if err != nil {
+				if err.Error() == utils.ErrNoRow() {
+					utils.FileLog.Info("商户不存在")
+					continue
+				}
+				utils.FileLog.Info("获取商户信息失败, Err: " + err.Error())
+				continue
+			}
+			item.BusinessCode = businessInfo.BusinessCode
+			item.EtaBusinessId = businessInfo.EtaBusinessId
+		}
+		if _, ok := excelData[item.CountryCode+"_"+item.Mobile]; ok {
+			//将用户插入其中,然后退出当前循环,进入下一循环
+			//repeatUser = append(repeatUser, item)
+			continue
+		}
+		excelData[item.CountryCode+"_"+item.Mobile] = item.CountryCode
+		//没问题数据
+		excelUser = append(excelUser, item)
+	}
+	// 判断系统里该手机号是否已存在
+	mobiles := make([]string, 0)
+	for k, _ := range excelData {
+		mobile := strings.Split(k, "_")[1]
+		mobiles = append(mobiles, mobile)
+	}
+
+	// 查询系统里是否存在
+	existUserMap := make(map[string]struct{})
+	if len(mobiles) > 0 {
+		existUsers, e := models.GetUserByMobiles(mobiles)
+		if e != nil {
+			br.Msg = "查询用户失败"
+			br.ErrMsg = "查询用户失败,Err:" + e.Error()
+			return
+		}
+		for _, user := range existUsers {
+			existUserMap[user.CountryCode+"_"+user.Mobile] = struct{}{}
+		}
+	}
+	for _, v := range excelUser {
+		if _, ok := existUserMap[v.CountryCode+"_"+v.Mobile]; !ok {
+			addUser = append(addUser, v)
+		}
+	}
+	fmt.Println("表格中用户与系统中存在用户没有冲突")
+	//如果表格中用户与系统中存在用户 没有冲突,那么继续执行
+	for _, user := range addUser {
+		if user.RealName != "" && user.Mobile != "" {
+			userData := new(models.User)
+			userData.RealName = user.RealName
+			userData.UserName = user.Mobile
+			userData.Mobile = user.Mobile
+			userData.CountryCode = user.CountryCode
+			userData.Email = user.Email
+			userData.Position = user.Position
+			userData.PositionStatus = user.PositionStatus
+			userData.BusinessCode = user.BusinessCode
+			userData.EtaBusinessId = user.EtaBusinessId
+			userData.DepartmentName = user.DepartmentName
+			userData.CountryCode = user.CountryCode
+			userData.CreatedTime = time.Now()
+			userData.LastUpdatedTime = time.Now()
+			userData.Enabled = 1
+			userData.SellerId = sysUser.AdminId
+			userData.SellerName = sysUser.RealName
+			//判断该手机号、邮箱是否已经添加,如果已经添加,那么就不再添加
+			var key string
+			key = "user:mobile:" + userData.Mobile
+
+			isHas := utils.Rc.IsExist(key)
+			if isHas == false {
+				newId, err := models.AddUser(userData)
+				userData.UserId = int(newId)
+				if err != nil {
+					br.Msg = "导入失败"
+					br.ErrMsg = "导入失败,Err:" + err.Error()
+					return
+				}
+			}
+		}
+	}
+	/*defer func() {
+		os.Remove(path)
+	}()*/
+
+	br.Msg = "导入成功"
+	br.Ret = 200
+	br.Success = true
+}
+
+// @Title 移动用户至新的商户下
+// @Description 移动用户至新的商户下
+// @Param	request	body company.EditUserReq true "type json string"
+// @router /user/changeBusiness [post]
+func (this *EtaBusinessUserController) ChangeUserBusiness() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		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.ChangeUserBusinessReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	if req.UserId <= 0 {
+		br.Msg = "参数错误!"
+		return
+	}
+
+	if req.EtaBusinessId == 0 {
+		br.Msg = "请选择商户"
+		br.IsSendEmail = false
+		return
+	}
+	//操作权限校验
+	userInfo, err := models.GetUserByUserId(req.UserId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			br.Msg = "用户不存在"
+			return
+		}
+		br.ErrMsg = "获取信息失败,Err:" + err.Error()
+		br.Msg = "获取信息失败"
+		return
+	}
+	if userInfo.EtaBusinessId == req.EtaBusinessId {
+		br.Msg = "用户已经在该商户下"
+		return
+	}
+	businessObj := new(eta_business.EtaBusiness)
+
+	businessInfo, err := businessObj.GetItemById(req.EtaBusinessId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			br.Msg = "商户不存在"
+			return
+		}
+		br.Msg = "商户信息有误"
+		br.ErrMsg = "获取商户信息失败, Err: " + err.Error()
+		return
+	}
+
+	//待更新字段
+	updateCol := []string{"LastUpdatedTime", "EtaBusinessId", "BusinessCode"}
+
+	userInfo.LastUpdatedTime = time.Now()
+	userInfo.EtaBusinessId = businessInfo.EtaBusinessId
+	userInfo.BusinessCode = businessInfo.BusinessCode
+	err = userInfo.Update(updateCol)
+	if err != nil {
+		br.Msg = "操作失败!"
+		br.ErrMsg = "操作失败,Err:" + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+	br.IsAddLog = true
+}
+
+// @Title 开启/禁用系统用户
+// @Description 开启/禁用系统用户接口
+// @Param	request	body system.SysuserEditReq true "type json string"
+// @Success 200 操作成功
+// @router /user/editEnabled [post]
+func (this *EtaBusinessUserController) EditEnabled() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req models.UserEditEnabledReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	if req.UserId <= 0 {
+		br.Msg = "请选择用户"
+		return
+	}
+
+	//操作权限校验
+	userInfo, err := models.GetUserByUserId(req.UserId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			br.Msg = "用户不存在"
+			return
+		}
+		br.ErrMsg = "获取信息失败,Err:" + err.Error()
+		br.Msg = "获取信息失败"
+		return
+	}
+	if req.Enabled != 0 && req.Enabled != 1 {
+		br.Msg = "参数错误"
+		return
+	}
+	//待更新字段
+	updateCol := []string{"LastUpdatedTime", "Enabled"}
+
+	userInfo.LastUpdatedTime = time.Now()
+
+	userInfo.Enabled = req.Enabled
+	err = userInfo.Update(updateCol)
+	if err != nil {
+		br.Msg = "操作失败!"
+		br.ErrMsg = "操作失败,Err:" + err.Error()
+		return
+	}
+
+	//todo 用户被禁用的情况下,需要将他对应的token给过期
+	/*if adminItem.Enabled == 1 && req.Enabled == 0 {
+		logOutSystemUser(adminItem.AdminId)
+	}*/
+
+	br.Ret = 200
+	br.Success = true
+	br.IsAddLog = true
+	br.Msg = "操作成功"
+}
+
+// AreaCodeList
+// @Title 手机号区号列表
+// @Description 手机号区号列表
+// @Success 200 Ret=200 获取成功
+// @router /area_code/list [get]
+func (this *EtaBusinessUserController) AreaCodeList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	type AreaCodeListResp struct {
+		Name  string `description:"地区"`
+		Value string `description:"区号"`
+	}
+	resp := make([]AreaCodeListResp, 0)
+	confAuth, e := system.GetCrmConfigDetailByCode(system.ConfAreaCodeListKey)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取手机号区号配置失败, Err: " + e.Error()
+		return
+	}
+	if confAuth.ConfigValue == "" {
+		br.Msg = "获取失败"
+		br.ErrMsg = "手机号区号配置为空"
+		return
+	}
+	if e := json.Unmarshal([]byte(confAuth.ConfigValue), &resp); e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "手机号区号配置有误"
+		return
+	}
+
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// TemplateDownload
+// @Title 下载模板
+// @Description 下载模板
+// @Success 200 {object} models.EdbdataClassifyResp
+// @Param   Source   query   int  false       "来源:1:模板1;2:模板2"
+// @router /user/template [get]
+func (this *EtaBusinessUserController) TemplateDownload() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	this.Ctx.Output.Download("./static/template/用户导入模版.xlsx", "用户导入模版.xlsx")
+	//this.Ctx.Output.Download("./static/数据导入模板.xlsx", "数据导入模板.xlsx")
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "下载成功"
+}

+ 726 - 0
controllers/eta_training_video/eta_training_video.go

@@ -0,0 +1,726 @@
+package eta_training_video
+
+import (
+	"encoding/json"
+	"eta/eta_forum_admin/controllers"
+	"eta/eta_forum_admin/models"
+	"eta/eta_forum_admin/models/eta_training_video"
+	etaTrainingVideoService "eta/eta_forum_admin/services/eta_training_video"
+	"eta/eta_forum_admin/utils"
+	"fmt"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"html/template"
+	"strings"
+	"time"
+)
+
+// EtaTrainingVideoController ETA培训视频
+type EtaTrainingVideoController struct {
+	controllers.BaseAuthController
+}
+
+// PageList
+// @Title 视频列表-分页
+// @Description 视频列表-分页
+// @Param   PageSize		query   int		true	"每页数据量"
+// @Param   CurrentIndex	query   int		true	"当前页码"
+// @Param   Keyword			query	string	false	"关键词: 视频名称/社会信用码/视频编码"
+// @Param   StartTime		query	string	false	"创建时间区间: 开始时间"
+// @Param   EndTime			query	string	false	"创建时间区间: 结束时间"
+// @Param   ClassifyId		query	int		false	"分类ID"
+// @Param   TagIds			query	string	false	"标签IDs, 英文逗号拼接"
+// @Param   PublishState	query	int		false	"发布状态: 1-未发布; 2-已发布"
+// @Success 200 Ret=200 获取成功
+// @router /page_list [get]
+func (this *EtaTrainingVideoController) PageList() {
+	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")
+	var startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = utils.StartIndex(currentIndex, pageSize)
+
+	// 筛选项
+	cond := ``
+	pars := make([]interface{}, 0)
+	{
+		// 关键词
+		keyword := this.GetString("Keyword", "")
+		keyword = strings.TrimSpace(keyword)
+		keyword = template.HTMLEscapeString(keyword)
+		if keyword != "" {
+			kw := fmt.Sprint("%", keyword, "%")
+			cond += fmt.Sprintf(` AND (%s LIKE ?)`, eta_training_video.VideoColumns.Title)
+			pars = append(pars, kw)
+		}
+
+		// 创建时间
+		startTime := this.GetString("StartTime", "")
+		endTime := this.GetString("EndTime", "")
+		if startTime != "" && endTime != "" {
+			st := fmt.Sprint(startTime, " 00:00:00")
+			ed := fmt.Sprint(endTime, " 23:59:59")
+			cond += fmt.Sprintf(` AND (%s BETWEEN ? AND ?)`, eta_training_video.VideoColumns.CreateTime)
+			pars = append(pars, st, ed)
+		}
+
+		// 分类ID
+		classifyId, _ := this.GetInt("ClassifyId", 0)
+		if classifyId > 0 {
+			cond += fmt.Sprintf(` AND FIND_IN_SET(?, %s)`, eta_training_video.VideoColumns.ClassifyIds)
+			pars = append(pars, classifyId)
+		}
+
+		// 标签IDs
+		strTagIds := this.GetString("TagIds", "")
+		if strTagIds != "" {
+			strTags := strings.Split(strTagIds, ",")
+			if len(strTags) > 0 {
+				joinArr := make([]string, 0)
+				cond += ` AND (`
+				for _, s := range strTags {
+					joinArr = append(joinArr, fmt.Sprintf(`FIND_IN_SET(?, %s)`, eta_training_video.VideoColumns.TagIds))
+					pars = append(pars, s)
+				}
+				cond += strings.Join(joinArr, ` OR `)
+				cond += `)`
+			} else {
+				cond += ` AND 1=2`
+			}
+		}
+
+		// 发布状态
+		publishState, _ := this.GetInt("PublishState", 0)
+		if publishState > 0 {
+			stateMap := map[int]int{1: 0, 2: 1}
+			cond += fmt.Sprintf(` AND %s = ?`, eta_training_video.VideoColumns.PublishState)
+			pars = append(pars, stateMap[publishState])
+		}
+	}
+
+	// 获取列表
+	videoOb := new(eta_training_video.EtaTrainingVideo)
+	total, e := videoOb.GetCountByCondition(cond, pars)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取视频总数失败, Err: " + e.Error()
+		return
+	}
+	list, e := videoOb.GetPageItemsByCondition(cond, pars, []string{}, "", startSize, pageSize)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取视频列表失败, Err: " + e.Error()
+		return
+	}
+	items, e := etaTrainingVideoService.FormatVideosToVideoItems(list)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = e.Error()
+		return
+	}
+
+	resp := new(eta_training_video.EtaTrainingVideoListResp)
+	resp.List = items
+	resp.Paging = paging.GetPaging(currentIndex, pageSize, total)
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// Add
+// @Title 新增视频
+// @Description 新增视频
+// @Param	request	body eta_training_video.EtaTrainingVideoAddReq true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /add [post]
+func (this *EtaTrainingVideoController) Add() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	// 参数校验
+	var req eta_training_video.EtaTrainingVideoAddReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	if req.ClassifyId <= 0 {
+		br.Msg = "请选择视频分类"
+		return
+	}
+	req.Title = strings.TrimSpace(req.Title)
+	if req.Title == "" {
+		br.Msg = "请输入视频名称"
+		return
+	}
+	if req.Introduce != "" {
+		req.Introduce = strings.TrimSpace(req.Introduce)
+		req.Introduce = template.HTMLEscapeString(req.Introduce)
+	}
+	req.CoverImg = strings.TrimSpace(req.CoverImg)
+	if req.CoverImg == "" {
+		br.Msg = "请上传封面图"
+		return
+	}
+	req.VideoUrl = strings.TrimSpace(req.VideoUrl)
+	if req.VideoUrl == "" {
+		br.Msg = "请上传视频"
+		return
+	}
+
+	// 分类
+	strClassifyIds := ""
+	classifyRelates := make([]*eta_training_video.EtaTrainingVideoClassifyRelate, 0)
+	{
+		classifyOB := new(eta_training_video.EtaTrainingVideoClassify)
+		classifies, e := classifyOB.GetItemsByCondition(``, make([]interface{}, 0), []string{}, "")
+		if e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = "获取分类失败, Err: " + e.Error()
+			return
+		}
+		for _, c := range classifies {
+			if c.EtaTrainingVideoClassifyId != req.ClassifyId {
+				continue
+			}
+			strClassifyIds += fmt.Sprintf("%d,", req.ClassifyId)
+			classifyRelates = append(classifyRelates, &eta_training_video.EtaTrainingVideoClassifyRelate{
+				EtaTrainingVideoClassifyId: c.EtaTrainingVideoClassifyId,
+				ClassifyParentId:           c.ParentId,
+				ClassifyName:               c.ClassifyName,
+			})
+			// 分类所有父级
+			if c.ParentId > 0 {
+				parents := etaTrainingVideoService.GetClassifyParentsRecursive(classifies, c.ParentId)
+				for _, p := range parents {
+					strClassifyIds += fmt.Sprintf("%d,", p.EtaTrainingVideoClassifyId)
+					classifyRelates = append(classifyRelates, &eta_training_video.EtaTrainingVideoClassifyRelate{
+						EtaTrainingVideoClassifyId: p.EtaTrainingVideoClassifyId,
+						ClassifyParentId:           p.ParentId,
+						ClassifyName:               p.ClassifyName,
+					})
+				}
+			}
+		}
+		strClassifyIds = strings.TrimRight(strClassifyIds, ",")
+	}
+
+	// 标签
+	strTagIds := ""
+	tagRelates := make([]*eta_training_video.EtaTrainingVideoTagRelate, 0)
+	if len(req.TagIds) > 0 {
+		tagOB := new(eta_training_video.EtaTrainingVideoTag)
+		tags, e := tagOB.GetItemsByCondition(``, make([]interface{}, 0), []string{}, "")
+		if e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = "获取标签失败, Err: " + e.Error()
+			return
+		}
+		tagMap := make(map[int]*eta_training_video.EtaTrainingVideoTag)
+		for _, t := range tags {
+			tagMap[t.EtaTrainingVideoTagId] = t
+		}
+		for _, r := range req.TagIds {
+			t := tagMap[r]
+			if t == nil {
+				continue
+			}
+			tagRelates = append(tagRelates, &eta_training_video.EtaTrainingVideoTagRelate{
+				EtaTrainingVideoTagId: r,
+				TagName:               t.TagName,
+			})
+			strTagIds += fmt.Sprintf("%d,", r)
+		}
+		strTagIds = strings.TrimRight(strTagIds, ",")
+	}
+
+	// 视频编码
+	now := time.Now().Local()
+	videoCode := utils.MD5(fmt.Sprint(now.UnixMicro()))
+
+	item := new(eta_training_video.EtaTrainingVideo)
+	item.VideoCode = videoCode
+	item.Title = req.Title
+	item.Introduce = req.Introduce
+	item.ClassifyIds = strClassifyIds
+	item.TagIds = strTagIds
+	item.CoverImg = req.CoverImg
+	item.VideoUrl = req.VideoUrl
+	item.CreateTime = now
+	item.ModifyTime = now
+	if e := item.CreateVideoAndRelates(item, classifyRelates, tagRelates); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "新增视频及关联失败, Err: " + e.Error()
+		return
+	}
+
+	// 操作日志
+	go func() {
+		logItem := new(eta_training_video.EtaTrainingVideoOpLog)
+		logItem.EtaTrainingVideoId = item.EtaTrainingVideoId
+		logItem.SysUserId = sysUser.AdminId
+		logItem.SysRealName = sysUser.RealName
+		logItem.OpType = eta_training_video.VideoOpTypeAdd
+		logItem.OpData = string(this.Ctx.Input.RequestBody)
+		logItem.CreateTime = now
+		_ = logItem.Create()
+	}()
+
+	// 更新标签引用数
+	go func() {
+		tagOB := new(eta_training_video.EtaTrainingVideoTag)
+		for _, t := range tagRelates {
+			_ = tagOB.UpdateVideoTotal(t.EtaTrainingVideoTagId)
+		}
+	}()
+
+	br.Data = item.EtaTrainingVideoId
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// Edit
+// @Title 编辑视频
+// @Description 编辑视频
+// @Param	request	body eta_training_video.EtaTrainingVideoEditReq true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /edit [post]
+func (this *EtaTrainingVideoController) Edit() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	// 参数校验
+	var req eta_training_video.EtaTrainingVideoEditReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	if req.VideoId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("参数有误, 视频ID: %d", req.VideoId)
+		return
+	}
+	if req.ClassifyId <= 0 {
+		br.Msg = "请选择视频分类"
+		return
+	}
+	req.Title = strings.TrimSpace(req.Title)
+	if req.Title == "" {
+		br.Msg = "请输入视频名称"
+		return
+	}
+	if req.Introduce == "" {
+		req.Introduce = strings.TrimSpace(req.Introduce)
+		req.Introduce = template.HTMLEscapeString(req.Introduce)
+	}
+	req.CoverImg = strings.TrimSpace(req.CoverImg)
+	if req.CoverImg == "" {
+		br.Msg = "请上传封面图"
+		return
+	}
+	req.VideoUrl = strings.TrimSpace(req.VideoUrl)
+	if req.VideoUrl == "" {
+		br.Msg = "请上传视频"
+		return
+	}
+
+	ob := new(eta_training_video.EtaTrainingVideo)
+	item, e := ob.GetItemById(req.VideoId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "视频不存在, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取视频失败, Err: " + e.Error()
+		return
+	}
+
+	// 分类
+	strClassifyIds := ""
+	classifyRelates := make([]*eta_training_video.EtaTrainingVideoClassifyRelate, 0)
+	{
+		classifyOB := new(eta_training_video.EtaTrainingVideoClassify)
+		classifies, e := classifyOB.GetItemsByCondition(``, make([]interface{}, 0), []string{}, "")
+		if e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = "获取分类失败, Err: " + e.Error()
+			return
+		}
+		for _, c := range classifies {
+			if c.EtaTrainingVideoClassifyId != req.ClassifyId {
+				continue
+			}
+			strClassifyIds += fmt.Sprintf("%d,", req.ClassifyId)
+			classifyRelates = append(classifyRelates, &eta_training_video.EtaTrainingVideoClassifyRelate{
+				EtaTrainingVideoClassifyId: c.EtaTrainingVideoClassifyId,
+				ClassifyParentId:           c.ParentId,
+				ClassifyName:               c.ClassifyName,
+			})
+			// 分类所有父级
+			if c.ParentId > 0 {
+				parents := etaTrainingVideoService.GetClassifyParentsRecursive(classifies, c.ParentId)
+				for _, p := range parents {
+					strClassifyIds += fmt.Sprintf("%d,", p.EtaTrainingVideoClassifyId)
+					classifyRelates = append(classifyRelates, &eta_training_video.EtaTrainingVideoClassifyRelate{
+						EtaTrainingVideoClassifyId: p.EtaTrainingVideoClassifyId,
+						ClassifyParentId:           p.ParentId,
+						ClassifyName:               p.ClassifyName,
+					})
+				}
+			}
+		}
+		strClassifyIds = strings.TrimRight(strClassifyIds, ",")
+	}
+
+	// 标签
+	strTagIds := ""
+	tagRelates := make([]*eta_training_video.EtaTrainingVideoTagRelate, 0)
+	if len(req.TagIds) > 0 {
+		tagOB := new(eta_training_video.EtaTrainingVideoTag)
+		tags, e := tagOB.GetItemsByCondition(``, make([]interface{}, 0), []string{}, "")
+		if e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = "获取标签失败, Err: " + e.Error()
+			return
+		}
+		tagMap := make(map[int]*eta_training_video.EtaTrainingVideoTag)
+		for _, t := range tags {
+			tagMap[t.EtaTrainingVideoTagId] = t
+		}
+		for _, r := range req.TagIds {
+			t := tagMap[r]
+			if t == nil {
+				continue
+			}
+			tagRelates = append(tagRelates, &eta_training_video.EtaTrainingVideoTagRelate{
+				EtaTrainingVideoTagId: r,
+				TagName:               t.TagName,
+			})
+			strTagIds += fmt.Sprintf("%d,", r)
+		}
+		strTagIds = strings.TrimRight(strTagIds, ",")
+	}
+
+	now := time.Now().Local()
+	item.Title = req.Title
+	item.Introduce = req.Introduce
+	item.ClassifyIds = strClassifyIds
+	item.TagIds = strTagIds
+	item.CoverImg = req.CoverImg
+	item.VideoUrl = req.VideoUrl
+	item.ModifyTime = now
+	cols := []string{"Title", "Introduce", "ClassifyIds", "TagIds", "CoverImg", "VideoUrl", "ModifyTime"}
+	if e := item.UpdateVideoAndRelates(item, cols, classifyRelates, tagRelates); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "更新视频及关联失败, Err: " + e.Error()
+		return
+	}
+
+	// 操作日志
+	go func() {
+		logItem := new(eta_training_video.EtaTrainingVideoOpLog)
+		logItem.EtaTrainingVideoId = item.EtaTrainingVideoId
+		logItem.SysUserId = sysUser.AdminId
+		logItem.SysRealName = sysUser.RealName
+		logItem.OpType = eta_training_video.VideoOpTypeEdit
+		logItem.OpData = string(this.Ctx.Input.RequestBody)
+		logItem.CreateTime = now
+		_ = logItem.Create()
+	}()
+
+	// 更新标签引用数
+	go func() {
+		tagOB := new(eta_training_video.EtaTrainingVideoTag)
+		for _, t := range tagRelates {
+			_ = tagOB.UpdateVideoTotal(t.EtaTrainingVideoTagId)
+		}
+	}()
+
+	br.Data = item.EtaTrainingVideoId
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// Publish
+// @Title 发布/取消视频
+// @Description 发布/取消视频
+// @Param	request	body eta_training_video.EtaTrainingVideoPublishReq true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /publish [post]
+func (this *EtaTrainingVideoController) Publish() {
+	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 eta_training_video.EtaTrainingVideoPublishReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	if req.VideoId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprint("参数有误, 视频ID: ", req.VideoId)
+		return
+	}
+	if req.PublishState != 0 && req.PublishState != 1 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprint("参数有误, 发布状态: ", req.PublishState)
+		return
+	}
+
+	ob := new(eta_training_video.EtaTrainingVideo)
+	item, e := ob.GetItemById(req.VideoId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "视频不存在, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取ETA培训视频失败, Err: " + e.Error()
+		return
+	}
+	now := time.Now().Local()
+	item.PublishState = req.PublishState
+	item.ModifyTime = now
+	cols := []string{"PublishState", "ModifyTime"}
+	if item.PublishState == 1 {
+		item.PublishTime = now
+		cols = append(cols, "PublishTime")
+	}
+	if e := item.Update(cols); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "发布ETA培训视频失败, Err: " + e.Error()
+		return
+	}
+
+	// 操作日志
+	go func() {
+		logItem := new(eta_training_video.EtaTrainingVideoOpLog)
+		logItem.EtaTrainingVideoId = item.EtaTrainingVideoId
+		logItem.SysUserId = sysUser.AdminId
+		logItem.SysRealName = sysUser.RealName
+		opTypeMap := map[int]int{0: eta_training_video.VideoOpTypeCancelPublish, 1: eta_training_video.VideoOpTypePublish}
+		logItem.OpType = opTypeMap[req.PublishState]
+		logItem.OpData = string(this.Ctx.Input.RequestBody)
+		logItem.CreateTime = now
+		_ = logItem.Create()
+	}()
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// Remove
+// @Title 删除视频
+// @Description 删除视频
+// @Param	request	body eta_training_video.EtaTrainingVideoRemoveReq true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /remove [post]
+func (this *EtaTrainingVideoController) Remove() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	// 参数校验
+	var req eta_training_video.EtaTrainingVideoRemoveReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	if req.VideoId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprint("参数有误, 视频ID: ", req.VideoId)
+		return
+	}
+
+	ob := new(eta_training_video.EtaTrainingVideo)
+	item, e := ob.GetItemById(req.VideoId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Ret = 200
+			br.Success = true
+			br.Msg = "操作成功"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取ETA培训视频失败, Err: " + e.Error()
+		return
+	}
+	if e := item.Del(); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "删除ETA培训视频失败, Err: " + e.Error()
+		return
+	}
+
+	// 移除分类、标签关联, 更新标签引用数
+	go func() {
+		cr := new(eta_training_video.EtaTrainingVideoClassifyRelate)
+		_ = cr.RemoveRelateByVideoId(item.EtaTrainingVideoId)
+
+		tr := new(eta_training_video.EtaTrainingVideoTagRelate)
+		_ = tr.RemoveRelateByVideoId(item.EtaTrainingVideoId)
+
+		t := new(eta_training_video.EtaTrainingVideoTag)
+		_ = t.RemoveVideoTotalByVideoId(item.EtaTrainingVideoId)
+	}()
+
+	// 操作日志
+	go func() {
+		logItem := new(eta_training_video.EtaTrainingVideoOpLog)
+		logItem.EtaTrainingVideoId = item.EtaTrainingVideoId
+		logItem.SysUserId = sysUser.AdminId
+		logItem.SysRealName = sysUser.RealName
+		logItem.OpType = eta_training_video.VideoOpTypeRemove
+		b, e := json.Marshal(item)
+		if e != nil {
+			return
+		}
+		logItem.OpData = string(b)
+		logItem.CreateTime = time.Now().Local()
+		_ = logItem.Create()
+	}()
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// Detail
+// @Title 视频详情
+// @Description 视频详情
+// @Param   VideoId		query	int		false	"视频ID"
+// @Success 200 Ret=200 操作成功
+// @router /detail [get]
+func (this *EtaTrainingVideoController) Detail() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	videoId, _ := this.GetInt("VideoId", 0)
+	if videoId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprint("参数有误, 视频ID: ", videoId)
+		return
+	}
+
+	ob := new(eta_training_video.EtaTrainingVideo)
+	item, e := ob.GetItemById(videoId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "视频不存在, 请刷新页面"
+			return
+		}
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取ETA培训视频失败, Err: " + e.Error()
+		return
+	}
+
+	list := make([]*eta_training_video.EtaTrainingVideo, 0)
+	list = append(list, item)
+	formats, e := etaTrainingVideoService.FormatVideosToVideoItems(list)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "格式化视频信息失败, Err: " + e.Error()
+		return
+	}
+	result := new(eta_training_video.EtaTrainingVideoItem)
+	if len(formats) > 0 {
+		result = formats[0]
+	}
+
+	br.Data = result
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}

+ 404 - 0
controllers/eta_training_video/eta_training_video_classify.go

@@ -0,0 +1,404 @@
+package eta_training_video
+
+import (
+	"encoding/json"
+	"eta/eta_forum_admin/controllers"
+	"eta/eta_forum_admin/models"
+	"eta/eta_forum_admin/models/eta_training_video"
+	etaTrainingVideoService "eta/eta_forum_admin/services/eta_training_video"
+	"eta/eta_forum_admin/utils"
+	"fmt"
+	"html/template"
+	"sort"
+	"strings"
+	"time"
+)
+
+// EtaTrainingVideoClassifyController ETA培训视频分类
+type EtaTrainingVideoClassifyController struct {
+	controllers.BaseAuthController
+}
+
+// Tree
+// @Title 分类树
+// @Description 分类树
+// @Param   Keyword			query	string	false	"关键词: 分类名称"
+// @Success 200 Ret=200 获取成功
+// @router /classify/tree [get]
+func (this *EtaTrainingVideoClassifyController) Tree() {
+	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
+	}
+
+	resp := new(eta_training_video.EtaTrainingVideoClassifyResp)
+	resp.List = make([]*eta_training_video.EtaTrainingVideoClassifyItem, 0)
+
+	cond := ``
+	pars := make([]interface{}, 0)
+	// 关键词
+	keyword := this.GetString("Keyword", "")
+	keyword = strings.TrimSpace(keyword)
+	keyword = template.HTMLEscapeString(keyword)
+
+	// 获取所有分类
+	ob := new(eta_training_video.EtaTrainingVideoClassify)
+	classifies, e := ob.GetItemsByCondition(cond, pars, []string{}, ``)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取分类列表失败, Err: " + e.Error()
+		return
+	}
+
+	list := make([]*eta_training_video.EtaTrainingVideoClassify, 0)
+	if keyword != "" {
+		classifyMap := make(map[int]*eta_training_video.EtaTrainingVideoClassify)
+		parentMap := make(map[int]*eta_training_video.EtaTrainingVideoClassify)
+		existMap := make(map[int]bool)
+
+		for _, m := range classifies {
+			classifyMap[m.EtaTrainingVideoClassifyId] = m
+		}
+		for _, m := range classifies {
+			if m.ParentId > 0 {
+				parentMap[m.EtaTrainingVideoClassifyId] = classifyMap[m.ParentId]
+			}
+		}
+
+		// 遍历菜单, 取出跟关键词匹配的菜单(以后可能会更改成无限级分类, 所以相似度匹配在这里处理)
+		for _, m := range classifies {
+			if !strings.Contains(m.ClassifyName, keyword) {
+				continue
+			}
+			if existMap[m.EtaTrainingVideoClassifyId] {
+				continue
+			}
+			existMap[m.EtaTrainingVideoClassifyId] = true
+			list = append(list, m)
+
+			// 取出关键词所匹配的所有父级菜单
+			if m.ParentId > 0 {
+				parents := etaTrainingVideoService.GetClassifyParentsRecursive(classifies, m.ParentId)
+				for _, p := range parents {
+					if !existMap[p.EtaTrainingVideoClassifyId] {
+						existMap[p.EtaTrainingVideoClassifyId] = true
+						list = append(list, p)
+					}
+				}
+			}
+		}
+
+		sort.Slice(list, func(i, j int) bool {
+			return list[j].Sort > list[i].Sort
+		})
+	} else {
+		list = classifies
+	}
+
+	items := make([]*eta_training_video.EtaTrainingVideoClassifyItem, 0)
+	for _, v := range list {
+		t := &eta_training_video.EtaTrainingVideoClassifyItem{
+			ClassifyId:   v.EtaTrainingVideoClassifyId,
+			ClassifyName: v.ClassifyName,
+			ParentId:     v.ParentId,
+			Sort:         v.Sort,
+			Children:     make([]*eta_training_video.EtaTrainingVideoClassifyItem, 0),
+		}
+		items = append(items, t)
+	}
+
+	// 递归返回树形结构
+	items = etaTrainingVideoService.GetClassifyTreeRecursive(items, 0)
+
+	resp.List = items
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// Add
+// @Title 新增分类
+// @Description 新增分类
+// @Param	request	body eta_training_video.EtaTrainingVideoClassifyAddReq true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /classify/add [post]
+func (this *EtaTrainingVideoClassifyController) Add() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	// 参数校验
+	var req eta_training_video.EtaTrainingVideoClassifyAddReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	req.ClassifyName = strings.TrimSpace(req.ClassifyName)
+	if req.ClassifyName == "" {
+		br.Msg = "请输入分类名称"
+		return
+	}
+	nameRune := []rune(req.ClassifyName)
+	if len(nameRune) > 50 {
+		br.Msg = "分类名称不可超过50个字符"
+		return
+	}
+
+	// 重名校验
+	{
+		ob := new(eta_training_video.EtaTrainingVideoClassify)
+		cond := fmt.Sprintf(` AND %s = ? AND %s = ?`, eta_training_video.VideoClassifyColumns.ParentId, eta_training_video.VideoClassifyColumns.ClassifyName)
+		pars := make([]interface{}, 0)
+		pars = append(pars, req.ParentId, req.ClassifyName)
+		exist, e := ob.GetItemByCondition(cond, pars)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			br.Msg = "操作失败"
+			br.ErrMsg = "获取重名分类失败, Err: " + e.Error()
+			return
+		}
+		if exist != nil {
+			br.Msg = "分类名称已存在"
+			return
+		}
+	}
+
+	item := new(eta_training_video.EtaTrainingVideoClassify)
+	item.ClassifyName = req.ClassifyName
+	item.ParentId = req.ParentId
+	item.SysUserId = sysUser.AdminId
+	item.SysRealName = sysUser.RealName
+	item.CreateTime = time.Now().Local()
+	item.ModifyTime = time.Now().Local()
+	if e := item.Create(); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "新增分类失败, Err: " + e.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// Edit
+// @Title 编辑分类
+// @Description 编辑分类
+// @Param	request	body eta_training_video.EtaTrainingVideoTagEditReq true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /classify/edit [post]
+func (this *EtaTrainingVideoClassifyController) Edit() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	// 参数校验
+	var req eta_training_video.EtaTrainingVideoClassifyEditReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	if req.ClassifyId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("参数有误, 分类ID: %d", req.ClassifyId)
+		return
+	}
+	req.ClassifyName = strings.TrimSpace(req.ClassifyName)
+	if req.ClassifyName == "" {
+		br.Msg = "请输入分类名称"
+		return
+	}
+	nameRune := []rune(req.ClassifyName)
+	if len(nameRune) > 50 {
+		br.Msg = "分类名称不可超过50个字符"
+		return
+	}
+
+	ob := new(eta_training_video.EtaTrainingVideoClassify)
+	item, e := ob.GetItemById(req.ClassifyId)
+	if e != nil {
+		if e.Error() != utils.ErrNoRow() {
+			br.Msg = "分类不存在, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取分类失败, Err: " + e.Error()
+		return
+	}
+	if item.ParentId == req.ParentId && item.ClassifyName == req.ClassifyName {
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "操作成功"
+		return
+	}
+
+	// 重名校验
+	{
+		cond := fmt.Sprintf(` AND %s = ? AND %s = ?`, eta_training_video.VideoClassifyColumns.ParentId, eta_training_video.VideoClassifyColumns.ClassifyName)
+		pars := make([]interface{}, 0)
+		pars = append(pars, req.ParentId, req.ClassifyName)
+		exist, e := ob.GetItemByCondition(cond, pars)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			br.Msg = "操作失败"
+			br.ErrMsg = "获取重名分类失败, Err: " + e.Error()
+			return
+		}
+		if exist != nil && exist.EtaTrainingVideoClassifyId != item.EtaTrainingVideoClassifyId {
+			br.Msg = "分类已存在"
+			return
+		}
+	}
+
+	now := time.Now().Local()
+	item.ClassifyName = req.ClassifyName
+	item.ParentId = req.ParentId
+	item.ModifyTime = now
+	cols := []string{"ClassifyName", "ParentId", "ModifyTime"}
+	if e := item.Update(cols); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "更新分类失败, Err: " + e.Error()
+		return
+	}
+
+	// 更新关联表冗余
+	go func() {
+		relateOB := new(eta_training_video.EtaTrainingVideoClassifyRelate)
+		_ = relateOB.UpdateClassifyInfoByClassifyId(item.EtaTrainingVideoClassifyId, req.ParentId, req.ClassifyName)
+	}()
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// Remove
+// @Title 删除分类
+// @Description 删除分类
+// @Param	request	body eta_training_video.EtaTrainingVideoClassifyRemoveReq true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /classify/remove [post]
+func (this *EtaTrainingVideoClassifyController) Remove() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	// 参数校验
+	var req eta_training_video.EtaTrainingVideoClassifyRemoveReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	if req.ClassifyId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("参数有误, 分类ID: %d", req.ClassifyId)
+		return
+	}
+
+	ob := new(eta_training_video.EtaTrainingVideoClassify)
+	item, e := ob.GetItemById(req.ClassifyId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Ret = 200
+			br.Success = true
+			br.Msg = "操作成功"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取分类失败, Err: " + e.Error()
+		return
+	}
+
+	// 删除校验
+	{
+		// 子分类
+		childCond := fmt.Sprintf(` AND %s = ?`, eta_training_video.VideoClassifyColumns.ParentId)
+		childPars := make([]interface{}, 0)
+		childPars = append(childPars, item.EtaTrainingVideoClassifyId)
+		childTotal, e := ob.GetCountByCondition(childCond, childPars)
+		if e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = "获取子分类数失败, Err: " + e.Error()
+			return
+		}
+		if childTotal > 0 {
+			br.Msg = "该分类下已关联内容, 不可删除"
+			return
+		}
+
+		// 引用
+		useOB := new(eta_training_video.EtaTrainingVideoClassifyRelate)
+		useCond := fmt.Sprintf(` AND %s = ?`, eta_training_video.VideoClassifyRelateColumns.EtaTrainingVideoClassifyId)
+		usePars := make([]interface{}, 0)
+		usePars = append(usePars, item.EtaTrainingVideoClassifyId)
+		useTotal, e := useOB.GetCountByCondition(useCond, usePars)
+		if e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = "获取分类引用数失败, Err: " + e.Error()
+			return
+		}
+		if useTotal > 0 {
+			br.Msg = "该分类下已关联内容, 不可删除"
+			return
+		}
+	}
+
+	if e := item.Del(); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "删除分类失败, Err: " + e.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}

+ 346 - 0
controllers/eta_training_video/eta_training_video_tag.go

@@ -0,0 +1,346 @@
+package eta_training_video
+
+import (
+	"encoding/json"
+	"eta/eta_forum_admin/controllers"
+	"eta/eta_forum_admin/models"
+	"eta/eta_forum_admin/models/eta_training_video"
+	"eta/eta_forum_admin/utils"
+	"fmt"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"html/template"
+	"strings"
+	"time"
+)
+
+// EtaTrainingVideoTagController ETA培训视频标签
+type EtaTrainingVideoTagController struct {
+	controllers.BaseAuthController
+}
+
+// PageList
+// @Title 标签列表-分页
+// @Description 标签列表-分页
+// @Param   PageSize		query   int		true	"每页数据量"
+// @Param   CurrentIndex	query   int		true	"当前页码"
+// @Param   Keyword			query	string	false	"关键词: 标签名称"
+// @Success 200 Ret=200 获取成功
+// @router /tag/page_list [get]
+func (this *EtaTrainingVideoTagController) PageList() {
+	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")
+	var startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = utils.StartIndex(currentIndex, pageSize)
+
+	cond := ``
+	pars := make([]interface{}, 0)
+	// 关键词
+	keyword := this.GetString("Keyword", "")
+	keyword = strings.TrimSpace(keyword)
+	keyword = template.HTMLEscapeString(keyword)
+	if keyword != "" {
+		kw := fmt.Sprint("%", keyword, "%")
+		cond += fmt.Sprintf(` AND %s LIKE ?`, eta_training_video.VideoTagColumns.TagName)
+		pars = append(pars, kw)
+	}
+
+	// 获取列表
+	tagOB := new(eta_training_video.EtaTrainingVideoTag)
+	total, e := tagOB.GetCountByCondition(cond, pars)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取标签总数失败, Err: " + e.Error()
+		return
+	}
+	list, e := tagOB.GetPageItemsByCondition(cond, pars, []string{}, "", startSize, pageSize)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取标签列表失败, Err: " + e.Error()
+		return
+	}
+	items := make([]*eta_training_video.EtaTrainingVideoTagItem, 0)
+	for _, v := range list {
+		t := new(eta_training_video.EtaTrainingVideoTagItem)
+		t.TagId = v.EtaTrainingVideoTagId
+		t.TagName = v.TagName
+		t.VideoTotal = v.VideoTotal
+		t.CreateTime = utils.TimeTransferString(utils.FormatDateTime, v.CreateTime)
+		t.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, v.ModifyTime)
+		items = append(items, t)
+	}
+
+	resp := new(eta_training_video.EtaTrainingVideoTagPageListResp)
+	resp.List = items
+	resp.Paging = paging.GetPaging(currentIndex, pageSize, total)
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// Add
+// @Title 新增标签
+// @Description 新增标签
+// @Param	request	body eta_training_video.EtaTrainingVideoTagAddReq true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /tag/add [post]
+func (this *EtaTrainingVideoTagController) Add() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	// 参数校验
+	var req eta_training_video.EtaTrainingVideoTagAddReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	req.TagName = strings.TrimSpace(req.TagName)
+	if req.TagName == "" {
+		br.Msg = "请输入标签名称"
+		return
+	}
+	nameRune := []rune(req.TagName)
+	if len(nameRune) > 50 {
+		br.Msg = "标签名称不可超过50个字符"
+		return
+	}
+
+	// 重名校验
+	{
+		item := new(eta_training_video.EtaTrainingVideoTag)
+		cond := fmt.Sprintf(` AND %s = ?`, eta_training_video.VideoTagColumns.TagName)
+		pars := make([]interface{}, 0)
+		pars = append(pars, req.TagName)
+		exist, e := item.GetItemByCondition(cond, pars)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			br.Msg = "操作失败"
+			br.ErrMsg = "获取重名标签失败, Err: " + e.Error()
+			return
+		}
+		if exist != nil {
+			br.Msg = "标签名称已存在"
+			return
+		}
+	}
+
+	item := new(eta_training_video.EtaTrainingVideoTag)
+	item.TagName = req.TagName
+	item.SysUserId = sysUser.AdminId
+	item.SysRealName = sysUser.RealName
+	item.CreateTime = time.Now().Local()
+	item.ModifyTime = time.Now().Local()
+	if e := item.Create(); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "新增视频标签失败, Err: " + e.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// Edit
+// @Title 编辑标签
+// @Description 编辑标签
+// @Param	request	body eta_training_video.EtaTrainingVideoTagEditReq true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /tag/edit [post]
+func (this *EtaTrainingVideoTagController) Edit() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	// 参数校验
+	var req eta_training_video.EtaTrainingVideoTagEditReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	if req.TagId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("参数有误, 标签ID: %d", req.TagId)
+		return
+	}
+	req.TagName = strings.TrimSpace(req.TagName)
+	if req.TagName == "" {
+		br.Msg = "请输入标签名称"
+		return
+	}
+	nameRune := []rune(req.TagName)
+	if len(nameRune) > 50 {
+		br.Msg = "标签名称不可超过50个字符"
+		return
+	}
+
+	ob := new(eta_training_video.EtaTrainingVideoTag)
+	item, e := ob.GetItemById(req.TagId)
+	if e != nil {
+		if e.Error() != utils.ErrNoRow() {
+			br.Msg = "标签不存在, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取标签失败, Err: " + e.Error()
+		return
+	}
+	if item.TagName == req.TagName {
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "操作成功"
+		return
+	}
+
+	// 重名校验
+	{
+		cond := fmt.Sprintf(` AND %s = ?`, eta_training_video.VideoTagColumns.TagName)
+		pars := make([]interface{}, 0)
+		pars = append(pars, req.TagName)
+		exist, e := ob.GetItemByCondition(cond, pars)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			br.Msg = "操作失败"
+			br.ErrMsg = "获取重名标签失败, Err: " + e.Error()
+			return
+		}
+		if exist != nil && exist.EtaTrainingVideoTagId != item.EtaTrainingVideoTagId {
+			br.Msg = "标签已存在"
+			return
+		}
+	}
+
+	now := time.Now().Local()
+	item.TagName = req.TagName
+	item.ModifyTime = now
+	cols := []string{"TagName", "ModifyTime"}
+	if e := item.Update(cols); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "更新标签失败, Err: " + e.Error()
+		return
+	}
+
+	// 更新关联表冗余
+	go func() {
+		relateOB := new(eta_training_video.EtaTrainingVideoTagRelate)
+		_ = relateOB.UpdateTagNameByTagId(item.EtaTrainingVideoTagId, req.TagName)
+	}()
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// Remove
+// @Title 删除标签
+// @Description 删除标签
+// @Param	request	body eta_training_video.EtaTrainingVideoTagRemoveReq true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /tag/remove [post]
+func (this *EtaTrainingVideoTagController) Remove() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	// 参数校验
+	var req eta_training_video.EtaTrainingVideoTagRemoveReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	if req.TagId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprint("参数有误, 标签ID: ", req.TagId)
+		return
+	}
+
+	ob := new(eta_training_video.EtaTrainingVideoTag)
+	item, e := ob.GetItemById(req.TagId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Ret = 200
+			br.Success = true
+			br.Msg = "操作成功"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取标签失败, Err: " + e.Error()
+		return
+	}
+	if e := item.Del(); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "删除标签失败, Err: " + e.Error()
+		return
+	}
+
+	// 移除关联
+	go func() {
+		relateOB := new(eta_training_video.EtaTrainingVideoTagRelate)
+		_ = relateOB.RemoveRelateByTagId(item.EtaTrainingVideoTagId)
+	}()
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}

+ 1929 - 0
controllers/eta_trial.go

@@ -0,0 +1,1929 @@
+package controllers
+
+import (
+	"encoding/json"
+	"eta/eta_forum_admin/models"
+	"eta/eta_forum_admin/models/company"
+	"eta/eta_forum_admin/models/eta_trial"
+	"eta/eta_forum_admin/models/system"
+	"eta/eta_forum_admin/services"
+	"eta/eta_forum_admin/services/eta_forum_hub"
+	"eta/eta_forum_admin/utils"
+	"fmt"
+	"github.com/mozillazg/go-pinyin"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"sort"
+	"strconv"
+	"strings"
+	"time"
+)
+
+type ETATrialController struct {
+	BaseAuthController
+}
+
+// @Title 获取所有ETA试用客户列表
+// @Description 获取所有ETA试用客户列表 接口
+// @Param   PageSize   query   int  true       "每页数据条数"
+// @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
+// @Param   KeyWord   query   string  true       "搜索关键词"
+// @Param   IsOnlyMe   query   bool  false       "是否只看我的"
+// @Param   SortParam   query   string  false       "排序字段参数,用来排序的字段, 枚举值:'Expiration':账号到期时长 、 'ModifyTime':账号更新时间 、 'LastLoginTime':最近一次登陆时间 、`ActiveTime:累计活跃时长` 、`IndexNum:累计添加指标` 、`ChartNum:累计添加图表`、`LoginNum:累计登录次数`"
+// @Param   SortType   query   string  true       "如何排序,是正序还是倒序,枚举值:`asc 正序`,`desc 倒叙`"
+// @Success 200 {object} models.ETATrialListRespList
+// @router /list [get]
+func (this *ETATrialController) List() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	isOnlyMe, _ := this.GetBool("IsOnlyMe")
+	pageSize, _ := this.GetInt("PageSize")
+	currentIndex, _ := this.GetInt("CurrentIndex")
+	keyWord := this.GetString("KeyWord")
+	keyWord = strings.Trim(keyWord, " ")
+	keyWord = strings.Replace(keyWord, "'", "", -1)
+
+	//排序参数
+	sortParam := this.GetString("SortParam")
+	sortType := this.GetString("SortType")
+
+	var total int
+	page := paging.GetPaging(currentIndex, pageSize, total)
+
+	var startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = paging.StartIndex(currentIndex, pageSize)
+	var condition string
+	var pars []interface{}
+	//优化需求257,放开销售也可以查看全部记录
+	//if sysUser.RoleTypeCode != "admin" && sysUser.RoleTypeCode != "ficc_admin" {
+	//	condition += ` AND seller_id = ? `
+	//	pars = append(pars, sysUser.AdminId)
+	//} else
+	if isOnlyMe {
+		condition += ` AND seller_id = ? `
+		pars = append(pars, sysUser.AdminId)
+	}
+
+	if keyWord != "" {
+		condition += ` AND (user_name LIKE '%` + keyWord + `%' OR company_name LIKE '%` + keyWord + `%' ) `
+	}
+
+	total, err := eta_trial.GetETATrialListCount(condition, pars)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据总数失败,Err:" + err.Error()
+		return
+	}
+
+	sortStr := ``
+	if sortParam != "" && sortType != "" {
+		if sortParam == "Expiration" {
+			if sortType == "asc" {
+				sortStr = "ORDER BY modify_time ASC "
+			} else {
+				sortStr = "ORDER  BY modify_time DESC "
+			}
+		} else if sortParam == "ModifyTime" {
+			if sortType == "asc" {
+				sortStr = "ORDER BY modify_time ASC "
+			} else {
+				sortStr = "ORDER  BY modify_time DESC "
+			}
+		} else if sortParam == "LastLoginTime" {
+			if sortType == "asc" {
+				sortStr = "ORDER BY last_login_time ASC "
+			} else {
+				sortStr = "ORDER  BY last_login_time DESC "
+			}
+		} else if sortParam == "ActiveTime" {
+			if sortType == "asc" {
+				sortStr = "ORDER BY active_time ASC"
+			} else {
+				sortStr = "ORDER  BY active_time DESC"
+			}
+		} else if sortParam == "IndexNum" {
+			if sortType == "asc" {
+				sortStr = "ORDER BY index_num ASC"
+			} else {
+				sortStr = "ORDER  BY index_num DESC"
+			}
+		} else if sortParam == "ChartNum" {
+			if sortType == "asc" {
+				sortStr = "ORDER BY chart_num ASC"
+			} else {
+				sortStr = "ORDER  BY chart_num DESC"
+			}
+		} else if sortParam == "LoginNum" {
+			if sortType == "asc" {
+				sortStr = "ORDER BY login_num ASC"
+			} else {
+				sortStr = "ORDER  BY login_num DESC"
+			}
+		} else if sortParam == "LastLoginDuration" {
+			if sortType == "asc" {
+				sortStr = "ORDER BY last_login_duration ASC"
+			} else {
+				sortStr = "ORDER  BY last_login_duration DESC"
+			}
+		}
+	} else {
+		sortStr = "ORDER BY create_time DESC "
+	}
+
+	list, err := eta_trial.GetETATrialList(condition, sortStr, pars, startSize, pageSize)
+	if err != nil {
+		br.Msg = "获取权限列表失败"
+		br.ErrMsg = "获取权限列表失败,Err:" + err.Error()
+		return
+	}
+
+	mobileSlice := make([]string, 0)
+	for _, item := range list {
+		activeTime, _ := strconv.Atoi(item.ActiveTime)
+		item.ActiveTime = utils.GetDurationFormatBySecond(activeTime)
+
+		// 最近一次登录时长
+		loginDuration, _ := strconv.Atoi(item.LastLoginDuration)
+		item.LastLoginDuration = utils.GetDurationFormatBySecond(loginDuration)
+
+		//到期时间
+		modifyTime, err := time.Parse(utils.FormatDateTime, item.ModifyTime)
+		if err != nil {
+			br.Msg = "时间转化失败"
+			br.ErrMsg = "时间转化失败,Err:" + err.Error()
+			return
+		}
+
+		var expired int
+
+		expiredTime := modifyTime.AddDate(0, 0, 14).Format(utils.FormatDate)
+		expired, err = utils.GetDaysBetween2Date(utils.FormatDate, expiredTime, time.Now().Format(utils.FormatDate))
+		if err != nil {
+			br.Msg = "时间计算失败"
+			br.ErrMsg = "时间计算失败,CalculationDate Err:" + err.Error()
+			return
+		}
+		item.Expiration = expired
+		mobileSlice = append(mobileSlice, "'"+item.Mobile+"'")
+	}
+
+	if len(mobileSlice) > 0 {
+		mobiles := strings.Join(mobileSlice, ",")
+		recordList, err := eta_trial.GetEtaTrialRecordParts(mobiles)
+		if err != nil {
+			br.Msg = "获取记录失败"
+			br.ErrMsg = "获取记录失败,GetEtaTrialRecordParts Err:" + err.Error()
+			return
+		}
+
+		recordMap := make(map[string]string)
+		for _, v := range recordList {
+			if _, ok := recordMap[v.Mobile]; !ok {
+				recordMap[v.Mobile] = v.Part
+			}
+		}
+
+		for _, item := range list {
+			item.InterestModule = recordMap[item.Mobile]
+		}
+	}
+
+	//待审批数量
+	condition = ` AND approval_status = '待审批' `
+	pars = []interface{}{}
+	totalApproval, err := eta_trial.GetETATrialApprovalListCount(condition, pars)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据总数失败,Err:" + err.Error()
+		return
+	}
+
+	page = paging.GetPaging(currentIndex, pageSize, total)
+	resp := new(eta_trial.ETATrialListRespList)
+	resp.List = list
+	resp.Paging = page
+	resp.ApprovalNum = totalApproval
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+
+	return
+}
+
+// @Title 新增用户
+// @Description 新增用户 接口
+// @Success 200 {object} models.ETATrialAddReq
+// @router /add [post]
+func (this *ETATrialController) Add() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		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 eta_trial.ETATrialAddReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	resp := new(eta_trial.ETATrialListResp)
+
+	//提交审批
+	approvalIds := make([]int, 0)
+	for _, v := range req.List {
+		if v.CompanyName == "" {
+			br.Msg = "请填写公司名称"
+			return
+		}
+		if v.UserName == "" {
+			br.Msg = "请填写客户名称"
+			return
+		}
+		if v.Position == "" {
+			br.Msg = "职务不可为空"
+			return
+		}
+		if v.Mobile == "" {
+			br.Msg = "手机号不可为空"
+			return
+		}
+
+		//检查是否为禁用客户
+		item, e := eta_trial.GetETATrialByMobile(v.Mobile)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			err = e
+		}
+		if item != nil {
+			if item.Enabled == 0 {
+				//放入被禁用客户列表中
+				respItem := eta_trial.ETATrialAddRespItem{
+					UserName:    v.UserName,
+					CompanyName: v.CompanyName,
+					Position:    v.Position,
+					Mobile:      v.Mobile,
+					Seller:      item.Seller,
+				}
+				resp.BannedList = append(resp.BannedList, &respItem)
+				continue
+			} else {
+				//客户已在正常使用中
+				//放入已提交申请列表中
+				respItem := eta_trial.ETATrialAddRespItem{
+					UserName:    v.UserName,
+					CompanyName: v.CompanyName,
+					Position:    v.Position,
+					Mobile:      v.Mobile,
+					Seller:      item.Seller,
+				}
+				resp.RepeatList = append(resp.RepeatList, &respItem)
+				continue
+			}
+		}
+
+		seller, e := system.GetSysAdminById(sysUser.AdminId)
+		if e != nil {
+			err = e
+			return
+		}
+
+		//检查是否历史已提交
+		approval, e := eta_trial.GetETATrialApprovalByMobile(v.Mobile)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			err = e
+		}
+		if approval != nil && approval.ApprovalStatus == "待审批" {
+			//处于待审批状态
+			//放入已提交申请列表中
+			respItem := eta_trial.ETATrialAddRespItem{
+				UserName:    v.UserName,
+				CompanyName: v.CompanyName,
+				Position:    v.Position,
+				Mobile:      v.Mobile,
+				Seller:      approval.Seller,
+			}
+			resp.RepeatList = append(resp.RepeatList, &respItem)
+			continue
+		}
+
+		//检查是否公司内部人员
+		mobileCount, e := eta_forum_hub.GetEtaTrialMobileCount(v.Mobile)
+		if e != nil {
+			br.Msg = "判断手机号是否存在失败"
+			br.ErrMsg = "判断手机号是否存在失败,Err:" + e.Error()
+			return
+		}
+		if mobileCount > 0 {
+			//放入内部人员列表中
+			respItem := eta_trial.ETATrialAddRespItem{
+				UserName:    v.UserName,
+				CompanyName: v.CompanyName,
+				Position:    v.Position,
+				Mobile:      v.Mobile,
+				Seller:      "",
+			}
+			resp.InternalList = append(resp.InternalList, &respItem)
+			continue
+		}
+
+		var approvalId int64
+		if approval != nil && (approval.ApprovalStatus == "驳回" || approval.ApprovalStatus == "已撤回") && v.Mobile == approval.Mobile {
+			//若是被驳回后重新申请且手机号相同的,更新审批记录
+			e = eta_trial.ResubmitTrialNoReasons(v.UserName, v.CompanyName, v.Position, approval.ApprovalId)
+			if e != nil {
+				err = e
+				return
+			}
+			approvalId = int64(approval.ApprovalId)
+		} else {
+			//加入审批列表
+			approvalItem := eta_trial.EtaTrialApproval{
+				UserName:        v.UserName,
+				CompanyName:     v.CompanyName,
+				Position:        v.Position,
+				Mobile:          v.Mobile,
+				SellerId:        seller.AdminId,
+				Seller:          seller.RealName,
+				CreateTime:      time.Now(),
+				ModifyTime:      time.Now(),
+				ApprovalContent: "申请账号",
+				ApplyMethod:     1,
+				ApprovalStatus:  "待审批",
+			}
+
+			approvalId, e = eta_trial.AddETATrialApproval(&approvalItem)
+			if e != nil {
+				err = e
+				return
+			}
+		}
+
+		//放入成功列表中
+		respItem := eta_trial.ETATrialAddRespItem{
+			UserName:    v.UserName,
+			CompanyName: v.CompanyName,
+			Position:    v.Position,
+			Mobile:      v.Mobile,
+			Seller:      seller.RealName,
+		}
+		resp.SuccessList = append(resp.SuccessList, &respItem)
+
+		//新增待办消息
+		msgItem := new(company.CompanyApprovalMessage)
+		msgItem.CreateUserId = sysUser.AdminId
+		msgItem.ReceiveUserId = -1
+		msgItem.MessageStatus = 1 // 已读, 不再亮红点, 但是消息要有
+		msgItem.Remark = seller.RealName + "申请试用账号"
+		msgItem.Content = seller.RealName + "申请试用账号"
+		msgItem.CompanyName = v.CompanyName
+		msgItem.CreateTime = time.Now()
+		msgItem.ModifyTime = time.Now()
+		msgItem.CompanyApprovalId = int(approvalId)
+		msgItem.OperationStatus = 1
+		msgItem.MessageType = 1    //1:申请消息,2:审批结果,3:文字消息
+		msgItem.SourceType = 9     //消息来源
+		msgItem.ApprovalStatus = 1 //审批状态,1:待审批,2:已审批,3:已驳回
+		err = company.AddCompanyApprovalMessage(msgItem)
+
+		approvalIds = append(approvalIds, int(approvalId))
+	}
+
+	// 2023-08-01默认审批通过, 不再进行人工审批, 也不发送待办消息
+	if len(approvalIds) > 0 {
+		go func() {
+			for _, v := range approvalIds {
+				_ = services.ApprovalApply(v)
+			}
+		}()
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "提交成功"
+	br.Data = resp
+
+	return
+}
+
+// @Title 申请启用
+// @Description 申请启用 接口
+// @Success 200 {object} models.ETATrialAddReq
+// @router /apply/enable [post]
+func (this *ETATrialController) ApplyEnable() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		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 eta_trial.ETAAddApplyReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	seller, e := system.GetSysAdminById(sysUser.AdminId)
+	if e != nil {
+		err = e
+		return
+	}
+
+	var condition string
+	var pars []interface{}
+	if req.IsCheckAll {
+		if len(req.MobileList) > 0 {
+			condition += " and mobile not in (" + utils.GetOrmInReplace(len(req.MobileList)) + ")"
+			for _, mobile := range req.MobileList {
+				pars = append(pars, mobile)
+			}
+		}
+	} else {
+		if len(req.MobileList) > 0 {
+			condition += " and mobile in (" + utils.GetOrmInReplace(len(req.MobileList)) + ")"
+			for _, mobile := range req.MobileList {
+				pars = append(pars, mobile)
+			}
+		}
+	}
+	trialApprovalList, err := eta_trial.GetETATrialByCondition(condition, pars)
+	if err != nil {
+		return
+	}
+
+	for _, trialApproval := range trialApprovalList {
+		approval, err := eta_trial.GetETATrialApprovalByMobile(trialApproval.Mobile)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			br.Msg = "获取失败!"
+			br.ErrMsg = "获取失败,Err:" + err.Error()
+			return
+		}
+
+		var approvalId int64
+		if approval != nil && (approval.ApprovalStatus == "驳回" || approval.ApprovalStatus == "已撤回") && trialApproval.Mobile == approval.Mobile {
+			//若是被驳回或撤回后申请且手机号相同的,更新审批记录
+			err = eta_trial.ResubmitTrial(approval.ApprovalId, req.ApplyReasons)
+			if err != nil {
+				br.Msg = "更新失败!"
+				br.ErrMsg = "更新失败,Err:" + err.Error()
+				return
+			}
+			approvalId = int64(approval.ApprovalId)
+		} else if approval != nil && approval.ApprovalStatus == "待审批" && approval.ApprovalContent == "申请启用" {
+			br.Msg = "该客户已在审批中!"
+			br.ErrMsg = "该客户已在审批中!"
+			return
+		} else {
+			//加入审批列表
+			approvalItem := eta_trial.EtaTrialApproval{
+				UserName:        trialApproval.UserName,
+				CompanyName:     trialApproval.CompanyName,
+				Position:        trialApproval.Position,
+				Mobile:          trialApproval.Mobile,
+				SellerId:        seller.AdminId,
+				Seller:          seller.RealName,
+				CreateTime:      time.Now(),
+				ModifyTime:      time.Now(),
+				ApprovalContent: "申请启用",
+				ApplyMethod:     2,
+				ApprovalStatus:  "待审批",
+				ApplyReasons:    req.ApplyReasons,
+			}
+
+			approvalId, e = eta_trial.AddETATrialApproval(&approvalItem)
+			if e != nil {
+				err = e
+				return
+			}
+		}
+
+		//新增待办消息
+		msgItem := new(company.CompanyApprovalMessage)
+		msgItem.CreateUserId = sysUser.AdminId
+		msgItem.ReceiveUserId = -1
+		msgItem.MessageStatus = 1 // 已读, 不再亮红点, 但是消息要有
+		msgItem.Remark = seller.RealName + "申请账号启用"
+		msgItem.Content = seller.RealName + "申请账号启用"
+		msgItem.CompanyName = trialApproval.CompanyName
+		msgItem.CreateTime = time.Now()
+		msgItem.ModifyTime = time.Now()
+		msgItem.CompanyApprovalId = int(approvalId)
+		msgItem.OperationStatus = 1
+		msgItem.MessageType = 1    //1:申请消息,2:审批结果,3:文字消息
+		msgItem.SourceType = 9     //消息来源
+		msgItem.ApprovalStatus = 1 //审批状态,1:待审批,2:已审批,3:已驳回
+		err = company.AddCompanyApprovalMessage(msgItem)
+
+		// 2023-08-01默认审批通过, 不再进行人工审批
+		go func() {
+			_ = services.ApprovalApply(int(approvalId))
+		}()
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "提交成功"
+}
+
+// @Title 撤回
+// @Description 撤回申请 接口
+// @Success 200 {object} models.ETATrialAddReq
+// @router /apply/revoke [post]
+func (this *ETATrialController) ApplyRevoke() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		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 eta_trial.RevokeReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	mobile := req.Mobile
+	mobile = strings.Trim(mobile, " ")
+	mobile = strings.Replace(mobile, "'", "", -1)
+
+	approval, err := eta_trial.GetETATrialApprovalByMobile(mobile)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			br.Msg = "待审批信息不存在"
+			br.ErrMsg = "待审批信息不存在,手机号:" + mobile
+			return
+		}
+		br.Msg = "获取信息失败"
+		br.ErrMsg = "获取信息失败,Err:" + err.Error()
+		return
+	}
+	if approval == nil {
+		br.Msg = "待审批信息不存在"
+		br.ErrMsg = "待审批信息不存在,手机号:" + mobile
+		return
+	}
+
+	if approval.ApprovalStatus != "待审批" {
+		br.Msg = "客户状态为:" + approval.ApprovalStatus + ";不可进行撤回操作"
+		br.ErrMsg = "客户状态为:" + approval.ApprovalStatus + ";不可进行撤回操作"
+		return
+	}
+	//撤回审批单
+	err = eta_trial.RevokeApproval(mobile)
+	if err != nil {
+		br.Msg = "撤回失败"
+		br.ErrMsg = "撤回失败,Err:" + err.Error()
+		return
+	}
+
+	// 更新审批消息状态为已读
+	go func() {
+		cond := ` AND company_approval_id = ? AND source_type = ? AND message_status = ?`
+		pars := make([]interface{}, 0)
+		pars = append(pars, approval.ApprovalId, 9, 0)
+		if e := company.ModifyCompanyApprovalMessageStatusByCond(cond, pars); e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = "更新销售ETA试用消息已读状态失败, Err: " + e.Error()
+			return
+		}
+	}()
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "撤回成功"
+}
+
+// @Title 删除申请
+// @Description 删除申请 接口
+// @Success 200 {object} models.ETATrialAddReq
+// @router /apply/del [post]
+func (this *ETATrialController) ApplyDel() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		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 eta_trial.DelReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.ApprovalId <= 0 {
+		br.Msg = "参数有误"
+		return
+	}
+	_, e := eta_trial.GetETATrialApprovalById(req.ApprovalId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "申请已被删除, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取ETA审批失败, Err: " + e.Error()
+		return
+	}
+
+	err = eta_trial.DelApproval(req.ApprovalId)
+	if err != nil {
+		br.Msg = "删除失败"
+		br.ErrMsg = "删除失败,Err:" + err.Error()
+		return
+	}
+
+	// 更新审批消息状态为已读
+	go func() {
+		cond := ` AND company_approval_id = ? AND source_type = ? AND message_status = ?`
+		pars := make([]interface{}, 0)
+		pars = append(pars, req.ApprovalId, 9, 0)
+		if e := company.ModifyCompanyApprovalMessageStatusByCond(cond, pars); e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = "更新销售ETA试用消息已读状态失败, Err: " + e.Error()
+			return
+		}
+	}()
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "删除成功"
+}
+
+// @Title 驳回申请
+// @Description 驳回申请 接口
+// @Param   ApprovalId   query   string  true       "审批id"
+// @Param   RejectReason   query   string  true       "驳回理由"
+// @Success 200 {object} models.ETATrialAddReq
+// @router /apply/reject [post]
+func (this *ETATrialController) ApplyReject() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		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 eta_trial.RejectReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	approvalId := req.ApprovalId
+	reason := req.RejectReason
+
+	err = eta_trial.RejectApproval(approvalId, reason)
+	if err != nil {
+		br.Msg = "删除失败"
+		br.ErrMsg = "删除失败,Err:" + err.Error()
+		return
+	}
+
+	approval, err := eta_trial.GetETATrialApprovalById(approvalId)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+
+	msgItem := new(company.CompanyApprovalMessage)
+	msgItem.CreateUserId = sysUser.AdminId
+	msgItem.ReceiveUserId = approval.SellerId
+	msgItem.MessageStatus = 0
+	msgItem.Remark = approval.UserName + approval.ApprovalContent + "驳回"
+	msgItem.Content = approval.UserName + approval.ApprovalContent + "驳回"
+	msgItem.CompanyName = approval.CompanyName
+	msgItem.CreateTime = time.Now()
+	msgItem.ModifyTime = time.Now()
+	msgItem.CompanyApprovalId = approvalId
+	msgItem.OperationStatus = 1
+	msgItem.MessageType = 2    //1:申请消息,2:审批结果,3:文字消息
+	msgItem.SourceType = 9     //消息来源
+	msgItem.ApprovalStatus = 3 //审批状态,1:待审批,2:已审批,3:已驳回
+	err = company.AddCompanyApprovalMessage(msgItem)
+
+	// 更新审批消息(发给所有管理员的)为已读
+	go func() {
+		cond := ` AND company_approval_id = ? AND source_type = ? AND message_status = ? AND receive_user_id = -1 `
+		pars := make([]interface{}, 0)
+		pars = append(pars, req.ApprovalId, 9, 0)
+		if e := company.ModifyCompanyApprovalMessageStatusByCond(cond, pars); e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = "更新销售ETA试用消息已读状态失败, Err: " + e.Error()
+			return
+		}
+	}()
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "驳回成功"
+}
+
+// @Title 同意申请
+// @Description 同意申请 接口
+// @Param   ApprovalId   query   string  true       "审批id"
+// @Success 200 {object} models.ETATrialAddReq
+// @router /apply/approval [post]
+func (this *ETATrialController) Approval() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		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 eta_trial.DelReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	approvalId := req.ApprovalId
+
+	err = eta_trial.ApprovalTrial(approvalId)
+	if err != nil {
+		br.Msg = "删除失败"
+		br.ErrMsg = "删除失败,Err:" + err.Error()
+		return
+	}
+
+	approval, err := eta_trial.GetETATrialApprovalById(approvalId)
+	if err != nil {
+		br.Msg = "查询失败"
+		br.ErrMsg = "查询失败,Err:" + err.Error()
+		return
+	}
+
+	//如果是新客户申请就新增,旧客户更改账号状态即可
+	if approval.ApplyMethod == 2 {
+		err := eta_trial.UpdateETATrialEnable(approval.Mobile)
+		if err != nil {
+			br.Msg = "启用失败"
+			br.ErrMsg = "启用失败,Err:" + err.Error()
+			return
+		}
+		err = eta_forum_hub.EnabledEtaTrialAdmin(approval.Mobile)
+		if err != nil {
+			br.Msg = "启用失败"
+			br.ErrMsg = "启用失败,Err:" + err.Error()
+			return
+		}
+	} else {
+		//新增客户
+		newItem := eta_trial.EtaTrial{
+			UserName:    approval.UserName,
+			CompanyName: approval.CompanyName,
+			Position:    approval.Position,
+			Mobile:      approval.Mobile,
+			Enabled:     1,
+			SellerId:    approval.SellerId,
+			Seller:      approval.Seller,
+			CreateTime:  time.Now(),
+			ModifyTime:  time.Now(),
+		}
+
+		strResult := ""
+		a := pinyin.NewArgs()
+		rows := pinyin.Pinyin(newItem.UserName, a)
+		for i := 0; i < len(rows); i++ {
+			strResult += rows[i][0]
+		}
+
+		//若非中文
+		if strResult == "" {
+			strResult = newItem.UserName
+		}
+		list, err := eta_trial.GetETATrialByAccount(strResult)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			br.Msg = "查询失败"
+			br.ErrMsg = "查询失败,GetETATrialByAccount Err:" + err.Error()
+			return
+		}
+		if list != nil && len(list) > 0 {
+			newItem.Account = strResult + strconv.Itoa(len(list))
+		} else {
+			newItem.Account = strResult
+		}
+
+		//newItem.Password = utils.GetRandStringNoSpecialChar(8)
+		// 初始密码固定
+		newItem.Password = "123456a"
+
+		_, err = eta_trial.AddETATrial(&newItem)
+		if err != nil {
+			br.Msg = "新增失败"
+			br.ErrMsg = "新增失败,AddETATrial Err:" + err.Error()
+			return
+		}
+
+		err = eta_forum_hub.AddEtaTrialAdmin(&newItem)
+		if err != nil {
+			err = fmt.Errorf("新增试用用户失败")
+			return
+		}
+	}
+
+	msgItem := new(company.CompanyApprovalMessage)
+	msgItem.CreateUserId = sysUser.AdminId
+	msgItem.ReceiveUserId = approval.SellerId
+	msgItem.MessageStatus = 0
+	msgItem.Remark = approval.UserName + approval.ApprovalContent + "通过"
+	msgItem.Content = approval.UserName + approval.ApprovalContent + "通过"
+	msgItem.CompanyName = approval.CompanyName
+	msgItem.CreateTime = time.Now()
+	msgItem.ModifyTime = time.Now()
+	msgItem.CompanyApprovalId = approvalId
+	msgItem.OperationStatus = 1
+	msgItem.MessageType = 2    //1:申请消息,2:审批结果,3:文字消息
+	msgItem.SourceType = 9     //消息来源
+	msgItem.ApprovalStatus = 2 //审批状态,1:待审批,2:已审批,3:已驳回
+	err = company.AddCompanyApprovalMessage(msgItem)
+
+	// 更新审批消息(发给所有管理员的)为已读
+	go func() {
+		cond := ` AND company_approval_id = ? AND source_type = ? AND message_status = ? AND receive_user_id = -1 `
+		pars := make([]interface{}, 0)
+		pars = append(pars, req.ApprovalId, 9, 0)
+		if e := company.ModifyCompanyApprovalMessageStatusByCond(cond, pars); e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = "更新销售ETA试用消息已读状态失败, Err: " + e.Error()
+			return
+		}
+	}()
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "审批成功"
+}
+
+// @Title 我的审批
+// @Description 我的审批 接口
+// @Param   PageSize   query   int  true       "每页数据条数"
+// @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
+// @Param   KeyWord   query   string  true       "搜索关键词"
+// @Param   SortType   query   string  true       "如何排序,是正序还是倒序,枚举值:`asc 正序`,`desc 倒叙`"
+// @router /apply/myList [get]
+func (this *ETATrialController) MyList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		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")
+	keyWord = strings.Trim(keyWord, " ")
+	keyWord = strings.Replace(keyWord, "'", "", -1)
+
+	//排序参数
+	sortType := this.GetString("SortType")
+
+	var total int
+	page := paging.GetPaging(currentIndex, pageSize, total)
+
+	var startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = paging.StartIndex(currentIndex, pageSize)
+	var condition string
+	var pars []interface{}
+
+	if keyWord != "" {
+		condition += ` AND (a.user_name LIKE '%` + keyWord + `%' OR a.company_name LIKE '%` + keyWord + `%' OR a.mobile LIKE '%` + keyWord + `%') `
+	}
+
+	sortStr := ""
+	if sortType == "asc" {
+		sortStr = "ORDER BY a.modify_time ASC "
+	} else {
+		sortStr = "ORDER BY a.modify_time DESC "
+	}
+
+	pars = append(pars, sysUser.AdminId)
+
+	total, err := eta_trial.GetETATrialApprovalCountBySellerId(condition, sortStr, pars)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据总数失败,Err:" + err.Error()
+		return
+	}
+
+	list, err := eta_trial.GetETATrialApprovalBySellerId(condition, sortStr, pars, startSize, pageSize)
+	if err != nil {
+		br.Msg = "查询失败"
+		br.ErrMsg = "查询失败,Err:" + err.Error()
+		return
+	}
+
+	page = paging.GetPaging(currentIndex, pageSize, total)
+	resp := new(eta_trial.ETATrialApprovalListRespList)
+	resp.List = list
+	resp.Paging = page
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+
+	return
+}
+
+// @Title 审批列表
+// @Description 审批列表 接口
+// @Param   SortParam   query   int  false       "排序字段参数,用来排序的字段  "
+// @Param   ListParam   query   int  false       "筛选字段参数,用来筛选的字段, 枚举值:1:全部 、 2:待审批 、 3:已审批  "
+// @Param   PageSize   query   int  true       "每页数据条数"
+// @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
+// @Param   KeyWord   query   string  true       "搜索关键词"
+// @Param   SortType   query   string  true       "如何排序,是正序还是倒序,枚举值:`asc 正序`,`desc 倒叙`"
+// @Success 200 {object} models.ETATrialAddReq
+// @router /apply/list [get]
+func (this *ETATrialController) ApplyList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		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")
+	keyWord = strings.Trim(keyWord, " ")
+	keyWord = strings.Replace(keyWord, "'", "", -1)
+
+	//排序参数
+	sortParam := this.GetString("SortParam")
+	listParam, _ := this.GetInt("ListParam")
+	sortType := this.GetString("SortType")
+
+	var total int
+	page := paging.GetPaging(currentIndex, pageSize, total)
+
+	var startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = paging.StartIndex(currentIndex, pageSize)
+	var condition string
+	var pars []interface{}
+
+	if keyWord != "" {
+		condition += ` AND (user_name LIKE '%` + keyWord + `%' OR company_name LIKE '%` + keyWord + `%' OR mobile LIKE '%` + keyWord + `%') `
+	}
+
+	sortStr := ""
+	if listParam == 0 {
+		condition += ` AND approval_status = '待审批' `
+	} else if listParam == 2 {
+		condition += ` AND approval_status = '待审批' `
+	} else if listParam == 3 {
+		condition += ` AND approval_status = '已审批' OR approval_status = '驳回' `
+	}
+
+	if sortType == "asc" {
+		sortStr = "ORDER BY modify_time ASC "
+	} else {
+		sortStr = "ORDER  BY modify_time DESC "
+	}
+	if sortParam == "Expiration" {
+		if sortType == "asc" {
+			sortStr = "ORDER BY modify_time ASC "
+		} else {
+			sortStr = "ORDER  BY modify_time DESC "
+		}
+	} else if sortParam == "ModifyTime" {
+		if sortType == "asc" {
+			sortStr = "ORDER BY modify_time ASC "
+		} else {
+			sortStr = "ORDER  BY modify_time DESC "
+		}
+	}
+
+	total, err := eta_trial.GetETATrialApprovalListCount(condition, pars)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据总数失败,Err:" + err.Error()
+		return
+	}
+
+	list, err := eta_trial.GetETATrialApprovalList(condition, sortStr, pars, startSize, pageSize)
+	if err != nil {
+		br.Msg = "查询失败"
+		br.ErrMsg = "查询失败,Err:" + err.Error()
+		return
+	}
+
+	page = paging.GetPaging(currentIndex, pageSize, total)
+	resp := new(eta_trial.ETATrialApprovalListRespList)
+	resp.List = list
+	resp.Paging = page
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+
+	return
+}
+
+// @Title 账号列表
+// @Description 账号列表 接口
+// @Param   SortParam   query   string  false       "排序字段参数,用来排序的字段, 枚举值:'Expiration':账号到期时长 、 'ModifyTime':账号更新时间 "
+// @Param   PageSize   query   int  true       "每页数据条数"
+// @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
+// @Param   KeyWord   query   string  true       "搜索关键词"
+// @Param   SortType   query   string  true       "如何排序,是正序还是倒序,枚举值:`asc 正序`,`desc 倒叙`"
+// @Success 200 {object} models.ETATrialAddReq
+// @router /apply/accountlist [get]
+func (this *ETATrialController) AccountList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		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")
+	keyWord = strings.Trim(keyWord, " ")
+	keyWord = strings.Replace(keyWord, "'", "", -1)
+
+	//排序参数
+	sortType := this.GetString("SortType")
+
+	var total int
+	page := paging.GetPaging(currentIndex, pageSize, total)
+
+	var startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = paging.StartIndex(currentIndex, pageSize)
+	var condition string
+	var pars []interface{}
+
+	if keyWord != "" {
+		condition += ` AND (user_name LIKE '%` + keyWord + `%' OR company_name LIKE '%` + keyWord + `%' OR mobile LIKE '%` + keyWord + `%') `
+	}
+
+	if sysUser.RoleTypeCode != "admin" && sysUser.RoleTypeCode != "ficc_admin" {
+		condition += ` AND seller_id = ? `
+		pars = append(pars, sysUser.AdminId)
+	}
+
+	total, err := eta_trial.GetETATrialListCount(condition, pars)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据总数失败,Err:" + err.Error()
+		return
+	}
+
+	sortStr := ""
+
+	if sortType == "asc" {
+		sortStr = "ORDER BY modify_time ASC "
+	} else {
+		sortStr = "ORDER  BY modify_time DESC "
+	}
+
+	list, err := eta_trial.GetETATrialList(condition, sortStr, pars, startSize, pageSize)
+	if err != nil {
+		br.Msg = "获取权限列表失败"
+		br.ErrMsg = "获取权限列表失败,Err:" + err.Error()
+		return
+	}
+
+	for _, item := range list {
+		activeTime, _ := strconv.Atoi(item.ActiveTime)
+		item.ActiveTime = utils.GetDurationFormatBySecond(activeTime)
+
+		//到期时间
+		modifyTime, err := time.Parse(utils.FormatDateTime, item.ModifyTime)
+		if err != nil {
+			br.Msg = "时间转化失败"
+			br.ErrMsg = "时间转化失败,Err:" + err.Error()
+			return
+		}
+
+		var expired int
+
+		expiredTime := modifyTime.AddDate(0, 0, 14).Format(utils.FormatDate)
+		expired, err = utils.GetDaysBetween2Date(utils.FormatDate, expiredTime, time.Now().Format(utils.FormatDate))
+		if err != nil {
+			br.Msg = "时间计算失败"
+			br.ErrMsg = "时间计算失败,CalculationDate Err:" + err.Error()
+			return
+		}
+		item.Expiration = expired
+	}
+
+	page = paging.GetPaging(currentIndex, pageSize, total)
+	resp := new(eta_trial.ETATrialListRespList)
+	resp.List = list
+	resp.Paging = page
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+
+	return
+}
+
+// @Title 问卷配置
+// @Description 问卷配置 接口
+// @Success 200 string  "获取成功"
+// @router /questionnaire/list [get]
+func (this *ETATrialController) QuestionnaireList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	cond := ""
+	lists, err := eta_trial.GetETATrialQuestionnaireList(cond)
+	if err != nil {
+		br.Msg = "获取问卷失败"
+		br.ErrMsg = "获取问卷失败,Err:" + err.Error()
+		return
+	}
+
+	var resp eta_trial.EtaTrialQuestionnaireResp
+	for _, v := range lists {
+		item := eta_trial.EtaTrialQuestionnaireRespItem{
+			QuestionnaireId: v.QuestionnaireId,
+			Question:        v.Question,
+			Type:            v.Type,
+			Sort:            v.Sort,
+			Options:         strings.Split(v.Options, "~#"),
+			IsMust:          v.IsMust,
+			CreateTime:      v.CreateTime.Format(utils.FormatDateTime),
+		}
+		resp.List = append(resp.List, item)
+	}
+
+	sort.Sort(resp)
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+
+	return
+}
+
+// @Title 问卷保存
+// @Description 问卷保存 接口
+// @Param	request	body eta_trial.EtaTrialQuestionnaireSaveReq true "type json string"
+// @Success 200 {object} models.ETATrialAddReq
+// @router /questionnaire/save [post]
+func (this *ETATrialController) QuestionnairesSave() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		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 eta_trial.EtaTrialQuestionnaireSaveReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	questionnaireList := make([]*eta_trial.EtaTrialQuestionnaire, 0)
+	for _, v := range req.List {
+		if v.QuestionnaireId == 0 {
+			//新增
+			questionItem := &eta_trial.EtaTrialQuestionnaire{
+				Question:   v.Question,
+				IsMust:     v.IsMust,
+				Type:       v.Type,
+				Sort:       v.Sort,
+				Options:    strings.Join(v.Options, "~#"),
+				CreateTime: time.Now(),
+			}
+			questionnaireList = append(questionnaireList, questionItem)
+		} else {
+			//更新
+			questionItem := &eta_trial.EtaTrialQuestionnaire{
+				QuestionnaireId: v.QuestionnaireId,
+				Question:        v.Question,
+				Options:         strings.Join(v.Options, "~#"),
+				Sort:            v.Sort,
+				IsMust:          v.IsMust,
+				CreateTime:      time.Time{},
+			}
+			cols := make([]string, 0)
+			cols = append(cols, "question", "options", "Sort", "IsMust")
+			err = questionItem.Update(cols)
+			if err != nil {
+				br.Msg = "更新问题失败!"
+				br.ErrMsg = "更新问题失败,Err:" + err.Error()
+				return
+			}
+		}
+	}
+
+	//批量新增问题和选项
+	if len(questionnaireList) > 0 {
+		err = eta_trial.AddETATrialQuestionnaire(questionnaireList)
+		if err != nil {
+			br.Msg = "保存问题失败!"
+			br.ErrMsg = "保存问题失败,Err:" + err.Error()
+			return
+		}
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "保存成功"
+
+	return
+}
+
+// @Title 问题删除
+// @Description 问题删除 接口
+// @Param	request	body eta_trial.EtaTrialQuestionnaireSaveReq true "type json string"
+// @Success 200 {object} models.ETATrialAddReq
+// @router /questionnaire/del [post]
+func (this *ETATrialController) QuestionnairesDel() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		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 eta_trial.EtaTrialQuestionnaireDelReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	if req.QuestionnaireId <= 0 {
+		br.Msg = "问题id异常!"
+		br.ErrMsg = "问题id异常,Err:" + err.Error()
+		return
+	}
+
+	count, err := eta_trial.GetETATrialQuestionnaireById(req.QuestionnaireId)
+	if err != nil && count > 0 {
+		br.Msg = "查询问题异常!"
+		br.ErrMsg = "查询问题异常,Err:" + err.Error()
+		return
+	}
+
+	err = eta_trial.DelETATrialQuestionnaireById(req.QuestionnaireId)
+	if err != nil && count > 0 {
+		br.Msg = "删除问题异常!"
+		br.ErrMsg = "删除问题异常,Err:" + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "删除成功"
+	return
+}
+
+// @Title 问卷统计结果
+// @Description 问卷统计结果 接口
+// @Param   ListParam   query   int  false       "筛选字段参数,用来筛选的字段, 枚举值:1:全部 、 2:选择题 、 3:简答题  "
+// @Success 200 {object} models.ETATrialAddReq
+// @router /questionnaire/statistical [get]
+func (this *ETATrialController) QuestionnairesStatistical() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	listParam, _ := this.GetInt("ListParam")
+
+	cond := ""
+	if listParam == 2 {
+		cond = "AND a.type = 1 OR a.type = 2 "
+	} else if listParam == 3 {
+		cond = "AND a.type = 3 "
+	}
+	list, err := eta_trial.GetETATrialQuestionnaireStatistic(cond)
+	if err != nil {
+		br.Msg = "获取问题统计失败!"
+		br.ErrMsg = "获取问题统计失败,Err:" + err.Error()
+		return
+	}
+
+	//获取单选和简答的回答数量,多选计算方式不同在计算后面统计时再计算
+	questionCountList, err := eta_trial.GetETATrialQuestionnaireStatisticCount()
+	if err != nil {
+		br.Msg = "获取问卷记录数量失败"
+		br.ErrMsg = "获取问卷记录数量失败,Err:" + err.Error()
+		return
+	}
+
+	questioncountMap := make(map[int]int, 0)
+	for _, v := range questionCountList {
+		questioncountMap[v.QuestionnaireId] = v.Count
+	}
+
+	chooseMap := make(map[string]int, 0)
+	answerCountMap := make(map[int]int, 0)
+	answerMap := make(map[int][]string, 0)
+	answerNameMap := make(map[int]string, 0)
+	for _, v := range list {
+		//问卷ID拼选项作为key,防止不同问题选项重复
+		if v.Type == 1 {
+			key := strconv.Itoa(v.QuestionnaireId) + v.Options
+			if _, ok := chooseMap[key]; !ok {
+				chooseMap[key] = v.Count
+			}
+		} else if v.Type == 2 {
+			options := strings.Split(v.Options, "~#")
+			for _, op := range options {
+				key := strconv.Itoa(v.QuestionnaireId) + op
+				if _, ok := chooseMap[key]; ok {
+					//多选题可能出现多人同样的选择
+					chooseMap[key] += v.Count
+				} else {
+					chooseMap[key] = v.Count
+				}
+				questioncountMap[v.QuestionnaireId] += v.Count
+			}
+		} else {
+			//简答题,最多十条
+			if count, ok := answerCountMap[v.QuestionnaireId]; ok && count < 11 {
+				answerMap[v.QuestionnaireId] = append(answerMap[v.QuestionnaireId], v.Options)
+				answerNameMap[v.QuestionnaireId] = v.UserName
+				answerCountMap[v.QuestionnaireId] += 1
+			} else if !ok {
+				answerMap[v.QuestionnaireId] = append(answerMap[v.QuestionnaireId], v.Options)
+				answerNameMap[v.QuestionnaireId] = v.UserName
+				answerCountMap[v.QuestionnaireId] = 1
+			}
+		}
+	}
+
+	lists, err := eta_trial.GetETATrialQuestionnaireList(cond)
+	if err != nil {
+		br.Msg = "获取问卷失败"
+		br.ErrMsg = "获取问卷失败,Err:" + err.Error()
+		return
+	}
+
+	var questionList eta_trial.EtaTrialQuestionnaireResp
+	for _, v := range lists {
+		item := eta_trial.EtaTrialQuestionnaireRespItem{
+			QuestionnaireId: v.QuestionnaireId,
+			Question:        v.Question,
+			Options:         strings.Split(v.Options, "~#"),
+			Type:            v.Type,
+			Sort:            v.Sort,
+			IsMust:          v.IsMust,
+			CreateTime:      v.CreateTime.Format(utils.FormatDateTime),
+		}
+		questionList.List = append(questionList.List, item)
+	}
+
+	sort.Sort(questionList)
+
+	var resp []eta_trial.EtaTrialQuestionnaireRecordStatisticResp
+	for _, v := range questionList.List {
+		if v.Type != 3 {
+			options := make([]*eta_trial.EtaTrialQuestionnaireRecordStatisticOption, 0)
+			for _, op := range v.Options {
+				key := strconv.Itoa(v.QuestionnaireId) + op
+				opItem := eta_trial.EtaTrialQuestionnaireRecordStatisticOption{
+					Option: op,
+					Count:  chooseMap[key],
+				}
+				options = append(options, &opItem)
+			}
+			item := eta_trial.EtaTrialQuestionnaireRecordStatisticResp{
+				QuestionnaireId: v.QuestionnaireId,
+				Question:        v.Question,
+				Options:         options,
+				Type:            v.Type,
+				IsMust:          v.IsMust,
+			}
+			resp = append(resp, item)
+		} else {
+			item := eta_trial.EtaTrialQuestionnaireRecordStatisticResp{
+				QuestionnaireId: v.QuestionnaireId,
+				Question:        v.Question,
+				Type:            v.Type,
+				IsMust:          v.IsMust,
+			}
+
+			ansList := make([]eta_trial.EtaTrialQuestionnaireRecordStatisticAnswer, 0)
+			for _, val := range answerMap[v.QuestionnaireId] {
+				ansItem := eta_trial.EtaTrialQuestionnaireRecordStatisticAnswer{
+					Answer:   val,
+					UserName: answerNameMap[v.QuestionnaireId],
+				}
+				ansList = append(ansList, ansItem)
+			}
+
+			item.Answers = ansList
+
+			resp = append(resp, item)
+		}
+	}
+
+	for _, v := range resp {
+		if v.Type != 3 && v.Type != 4 {
+			for i, op := range v.Options {
+				key := strconv.Itoa(v.QuestionnaireId) + op.Option
+				opCount := float64(chooseMap[key])
+				count := float64(questioncountMap[v.QuestionnaireId])
+				if count != 0 {
+					per, _ := strconv.ParseFloat(fmt.Sprintf("%.4f", opCount/count), 64)
+					op.Percentage = per
+				}
+				op.Index = i
+			}
+		}
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "保存成功"
+	br.Data = resp
+
+	return
+}
+
+// @Title 简答题列表
+// @Description 简答题列表 接口
+// @Success 200 string  "获取成功"
+// @Param   PageSize   query   int  true       "每页数据条数"
+// @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
+// @Param   QuestionnaireId   query   int  true       "问题id"
+// @router /questionnaire/answerList [get]
+func (this *ETATrialController) QuestionnaireAnswerList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	var startSize int
+	pageSize, _ := this.GetInt("PageSize")
+	currentIndex, _ := this.GetInt("CurrentIndex")
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = paging.StartIndex(currentIndex, pageSize)
+
+	questionnaireId, _ := this.GetInt("QuestionnaireId")
+	lists, err := eta_trial.GetETATrialQuestionnaireAnswerListById(questionnaireId, startSize, pageSize)
+	if err != nil {
+		br.Msg = "获取问卷失败"
+		br.ErrMsg = "获取问卷失败,Err:" + err.Error()
+		return
+	}
+
+	total, err := eta_trial.GetETATrialQuestionnaireAnswerListByIdCount(questionnaireId)
+	if err != nil {
+		br.Msg = "获取问卷失败"
+		br.ErrMsg = "获取问卷失败,Err:" + err.Error()
+		return
+	}
+	cond := ""
+	qLists, err := eta_trial.GetETATrialQuestionnaireList(cond)
+	if err != nil {
+		br.Msg = "获取问卷失败"
+		br.ErrMsg = "获取问卷失败,Err:" + err.Error()
+		return
+	}
+
+	mustMap := make(map[int]int, 0)
+	for _, v := range qLists {
+		mustMap[v.QuestionnaireId] = v.IsMust
+	}
+
+	resp := new(eta_trial.QuestionnaireDetailListResp)
+	for _, v := range lists {
+		item := eta_trial.EtaTrialQuestionnaireRecordRespItem{
+			RecordId:        v.RecordId,
+			UserName:        v.UserName,
+			CompanyName:     v.CompanyName,
+			Position:        v.Position,
+			Options:         v.Options,
+			Mobile:          v.Mobile,
+			Type:            v.Type,
+			QuestionnaireId: v.QuestionnaireId,
+			IsMust:          mustMap[v.QuestionnaireId],
+			CreateTime:      v.CreateTime.Format(utils.FormatDateTime),
+		}
+		resp.List = append(resp.List, &item)
+	}
+	page := paging.GetPaging(currentIndex, pageSize, total)
+
+	resp.Paging = page
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+
+	return
+}
+
+// @Title 详细数据列表
+// @Description 详细数据列表 接口
+// @Success 200 string  "获取成功"
+// @Param   PageSize   query   int  true       "每页数据条数"
+// @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
+// @router /questionnaire/detailList [get]
+func (this *ETATrialController) QuestionnaireDetailList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	var startSize int
+
+	pageSize, _ := this.GetInt("PageSize")
+	currentIndex, _ := this.GetInt("CurrentIndex")
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = paging.StartIndex(currentIndex, pageSize)
+
+	total, err := eta_trial.GetETATrialQuestionnaireDetailListCount()
+	if err != nil {
+		br.Msg = "获取问卷数量失败"
+		br.ErrMsg = "获取问卷数量失败,Err:" + err.Error()
+		return
+	}
+
+	if total == 0 {
+		br.Msg = "无问卷调查记录"
+		br.Ret = 200
+		br.Success = true
+		return
+	}
+
+	lists, err := eta_trial.GetETATrialQuestionnaireDetailList(startSize, pageSize)
+	if err != nil {
+		br.Msg = "获取问卷失败"
+		br.ErrMsg = "获取问卷失败,Err:" + err.Error()
+		return
+	}
+
+	cond := ""
+	qLists, err := eta_trial.GetETATrialQuestionnaireList(cond)
+	if err != nil {
+		br.Msg = "获取问卷失败"
+		br.ErrMsg = "获取问卷失败,Err:" + err.Error()
+		return
+	}
+
+	mustMap := make(map[int]int, 0)
+	for _, v := range qLists {
+		mustMap[v.QuestionnaireId] = v.IsMust
+	}
+	resp := new(eta_trial.QuestionnaireDetailListResp)
+
+	for _, v := range lists {
+		item := eta_trial.EtaTrialQuestionnaireRecordRespItem{
+			RecordId:        v.RecordId,
+			UserName:        v.UserName,
+			CompanyName:     v.CompanyName,
+			Position:        v.Position,
+			Options:         v.Options,
+			Mobile:          v.Mobile,
+			Type:            v.Type,
+			QuestionnaireId: v.QuestionnaireId,
+			IsMust:          mustMap[v.QuestionnaireId],
+			CreateTime:      v.CreateTime.Format(utils.FormatDateTime),
+		}
+		resp.List = append(resp.List, &item)
+	}
+
+	page := paging.GetPaging(currentIndex, pageSize, total)
+
+	resp.Paging = page
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+
+	return
+}
+
+// @Title 详细数据查看
+// @Description 详细数据查看 接口
+// @Success 200 string  "获取成功"
+// @Param   Mobile   query   string  true       "手机号"
+// @Param   CreateTime   query   string  true       "手机号"
+// @router /questionnaire/detail [get]
+func (this *ETATrialController) QuestionnaireDetail() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	cond := ""
+	lists, err := eta_trial.GetETATrialQuestionnaireList(cond)
+	if err != nil {
+		br.Msg = "获取问卷失败"
+		br.ErrMsg = "获取问卷失败,Err:" + err.Error()
+		return
+	}
+
+	var resp eta_trial.EtaTrialQuestionnaireResp
+	for _, v := range lists {
+		item := eta_trial.EtaTrialQuestionnaireRespItem{
+			QuestionnaireId: v.QuestionnaireId,
+			Question:        v.Question,
+			Options:         strings.Split(v.Options, "~#"),
+			Type:            v.Type,
+			Sort:            v.Sort,
+			IsMust:          v.IsMust,
+			CreateTime:      v.CreateTime.Format(utils.FormatDateTime),
+		}
+		resp.List = append(resp.List, item)
+	}
+
+	sort.Sort(resp)
+
+	mobile := this.GetString("Mobile")
+	createTime := this.GetString("CreateTime")
+
+	timeDate, _ := time.Parse(utils.FormatDateTime, createTime)
+	startDate := timeDate.AddDate(0, 0, -1).Format(utils.FormatDateTime)
+	endDate := timeDate.AddDate(0, 0, 1).Format(utils.FormatDateTime)
+	answerList, err := eta_trial.GetETATrialQuestionnaireDetailListByMobile(mobile, startDate, endDate)
+	if err != nil {
+		br.Msg = "获取问卷失败"
+		br.ErrMsg = "获取问卷失败,Err:" + err.Error()
+		return
+	}
+
+	answerMap := make(map[int]string)
+	for _, v := range answerList {
+		answerMap[v.QuestionnaireId] = v.Options
+	}
+
+	nResp := make([]eta_trial.EtaTrialQuestionnaireDetailItem, 0)
+	for _, v := range lists {
+		item := eta_trial.EtaTrialQuestionnaireDetailItem{
+			QuestionnaireId: v.QuestionnaireId,
+			Question:        v.Question,
+			Type:            v.Type,
+			Sort:            v.Sort,
+			IsMust:          v.IsMust,
+			Options:         strings.Split(v.Options, "~#"),
+			UserOptions:     answerMap[v.QuestionnaireId],
+			CreateTime:      v.CreateTime.Format(utils.FormatDateTime),
+		}
+		nResp = append(nResp, item)
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = nResp
+
+	return
+}
+
+// EnableAccountList
+// @Title 已启用账号列表
+// @Description 已启用账号列表
+// @Success 200
+// @router /enable/account/list [get]
+func (this *ETATrialController) EnableAccountList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	var condition string
+	var pars []interface{}
+
+	condition += ` AND enabled = ? `
+	pars = append(pars, 1)
+
+	adminItems, err := system.GetSysUserItems(condition, pars)
+	if err != nil {
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = adminItems
+	return
+}
+
+// AccountTransfer
+// @Title 账号转移
+// @Description 账号转移
+// @Success 200
+// @router /account/transfer [post]
+func (this *ETATrialController) AccountTransfer() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		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 eta_trial.TrialAccountTransferReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	if len(req.EtaTrialIdList) <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = "参数有误, 至少选中一个试用用户"
+		return
+	}
+
+	err = eta_trial.TransferETATrial(&req)
+	if err != nil {
+		br.Msg = "转移失败!"
+		br.ErrMsg = "转移失败,Err:" + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	return
+}

+ 401 - 0
controllers/eta_version_update_log.go

@@ -0,0 +1,401 @@
+package controllers
+
+import (
+	"encoding/json"
+	"eta/eta_forum_admin/models"
+	"eta/eta_forum_admin/utils"
+	"fmt"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// EtaVersionUpdateLogController ETA版本更新日志
+type EtaVersionUpdateLogController struct {
+	BaseAuthController
+}
+
+// PageList
+// @Title 更新日志列表-分页
+// @Description 更新日志列表-分页
+// @Param   Keyword		query	string	false	"关键词"
+// @Param   SortType	query	string	false	"排序方式, 1-ASC; 2-DESC"
+// @Success 200 Ret=200 获取成功
+// @router /page_list [get]
+func (this *EtaVersionUpdateLogController) PageList() {
+	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")
+	var startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = utils.StartIndex(currentIndex, pageSize)
+
+	cond := ``
+	pars := make([]interface{}, 0)
+	// 关键词
+	keyword := this.GetString("Keyword", "")
+	keyword = strings.TrimSpace(keyword)
+	if keyword != "" {
+		kw := fmt.Sprint("%", keyword, "%")
+		cond += ` AND content LIKE ?`
+		pars = append(pars, kw)
+	}
+
+	sortType, _ := this.GetInt("SortType", 0)
+	orderMap := map[int]string{0: "", 1: "update_date ASC", 2: "update_date DESC"}
+	order := orderMap[sortType]
+
+	// 获取列表
+	businessOb := new(models.EtaVersionUpdateLog)
+	total, e := businessOb.GetCountByCondition(cond, pars)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取更新日志总数失败, Err: " + e.Error()
+		return
+	}
+	list, e := businessOb.GetPageItemsByCondition(cond, pars, []string{}, order, startSize, pageSize)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取更新日志列表失败, Err: " + e.Error()
+		return
+	}
+
+	items := make([]*models.EtaVersionUpdateLogItem, 0)
+	for _, v := range list {
+		b := new(models.EtaVersionUpdateLogItem)
+		b.Id = v.Id
+		b.Version = v.Version
+		b.Content = v.Content
+		b.UpdateDate = v.UpdateDate.Format(utils.FormatDate)
+		b.CreateTime = v.CreateTime.Format(utils.FormatDateTime)
+		b.ModifyTime = v.ModifyTime.Format(utils.FormatDateTime)
+		items = append(items, b)
+	}
+
+	resp := new(models.EtaVersionUpdateLogListResp)
+	resp.List = items
+	resp.Paging = paging.GetPaging(currentIndex, pageSize, total)
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// Add
+// @Title 新增
+// @Description 新增
+// @Param	request	body models.EtaVersionUpdateLogAddReq true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /add [post]
+func (this *EtaVersionUpdateLogController) Add() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	// 参数校验
+	var req models.EtaVersionUpdateLogAddReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	req.Version = strings.TrimSpace(req.Version)
+	if req.Version == "" {
+		br.Msg = "版本号不可为空"
+		return
+	}
+	// 版本号格式: X.X.X
+	versionArr := strings.Split(req.Version, ".")
+	if len(versionArr) != 3 {
+		br.Msg = "请输入正确格式的版本号"
+		return
+	}
+	for _, n := range versionArr {
+		if _, e := strconv.Atoi(n); e != nil {
+			br.Msg = "请输入正确格式的版本号"
+			return
+		}
+	}
+
+	req.Content = strings.TrimSpace(req.Content)
+	if req.Content == "" {
+		br.Msg = "更新内容不可为空"
+		return
+	}
+	updateDate, e := time.ParseInLocation(utils.FormatDate, req.UpdateDate, time.Local)
+	if e != nil {
+		br.Msg = "更新日期格式有误"
+		br.ErrMsg = "更新日期格式有误, Err: " + e.Error()
+		return
+	}
+
+	// 版本号重复校验
+	{
+		item := new(models.EtaVersionUpdateLog)
+		cond := ` AND version = ?`
+		pars := make([]interface{}, 0)
+		pars = append(pars, req.Version)
+		exist, e := item.GetItemByCondition(cond, pars)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			br.Msg = "操作失败"
+			br.ErrMsg = "获取更新日志重复版本号失败, Err: " + e.Error()
+			return
+		}
+		if exist != nil {
+			br.Msg = "该版本号已有更新日志, 请重新输入"
+			return
+		}
+	}
+
+	// 更新日期重复校验
+	{
+		item := new(models.EtaVersionUpdateLog)
+		cond := ` AND update_date = ?`
+		pars := make([]interface{}, 0)
+		pars = append(pars, req.UpdateDate)
+		exist, e := item.GetItemByCondition(cond, pars)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			br.Msg = "操作失败"
+			br.ErrMsg = "获取更新日志重复日期失败, Err: " + e.Error()
+			return
+		}
+		if exist != nil {
+			br.Msg = "该日期已有更新日志, 请重新选择"
+			return
+		}
+	}
+
+	// 新增
+	item := new(models.EtaVersionUpdateLog)
+	item.Version = req.Version
+	item.UpdateDate = updateDate
+	item.Content = req.Content
+	item.CreateTime = time.Now().Local()
+	item.ModifyTime = time.Now().Local()
+	if e = item.Create(); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "新增更新日志失败, Err: " + e.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// Edit
+// @Title 编辑
+// @Description 编辑
+// @Param	request	body models.EtaVersionUpdateLogEditReq true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /edit [post]
+func (this *EtaVersionUpdateLogController) Edit() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	// 参数校验
+	var req models.EtaVersionUpdateLogEditReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	if req.Id <= 0 {
+		br.Msg = "参数有误"
+		return
+	}
+	req.Version = strings.TrimSpace(req.Version)
+	if req.Version == "" {
+		br.Msg = "版本号不可为空"
+		return
+	}
+	// 版本号格式: X.X.X
+	versionArr := strings.Split(req.Version, ".")
+	if len(versionArr) != 3 {
+		br.Msg = "请输入正确格式的版本号"
+		return
+	}
+	for _, n := range versionArr {
+		if _, e := strconv.Atoi(n); e != nil {
+			br.Msg = "请输入正确格式的版本号"
+			return
+		}
+	}
+	req.Content = strings.TrimSpace(req.Content)
+	if req.Content == "" {
+		br.Msg = "更新内容不可为空"
+		return
+	}
+	updateDate, e := time.ParseInLocation(utils.FormatDate, req.UpdateDate, time.Local)
+	if e != nil {
+		br.Msg = "更新日期格式有误"
+		br.ErrMsg = "更新日期格式有误, Err: " + e.Error()
+		return
+	}
+
+	// 版本号重复校验
+	{
+		item := new(models.EtaVersionUpdateLog)
+		cond := ` AND version = ?`
+		pars := make([]interface{}, 0)
+		pars = append(pars, req.Version)
+		exist, e := item.GetItemByCondition(cond, pars)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			br.Msg = "操作失败"
+			br.ErrMsg = "获取更新日志重复版本号失败, Err: " + e.Error()
+			return
+		}
+		if exist != nil && exist.Id != req.Id {
+			br.Msg = "该版本号已有更新日志, 请重新输入"
+			return
+		}
+	}
+
+	// 更新日期重复校验
+	{
+		item := new(models.EtaVersionUpdateLog)
+		cond := ` AND update_date = ?`
+		pars := make([]interface{}, 0)
+		pars = append(pars, req.UpdateDate)
+		exist, e := item.GetItemByCondition(cond, pars)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			br.Msg = "操作失败"
+			br.ErrMsg = "获取更新日志重复日期失败, Err: " + e.Error()
+			return
+		}
+		if exist != nil && exist.Id != req.Id {
+			br.Msg = "该日期已有更新日志, 请重新选择"
+			return
+		}
+	}
+
+	ob := new(models.EtaVersionUpdateLog)
+	item, e := ob.GetItemById(req.Id)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "更新日志不存在, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取更新日志信息失败, Err: " + e.Error()
+		return
+	}
+	item.Version = req.Version
+	item.Content = req.Content
+	item.UpdateDate = updateDate
+	item.ModifyTime = time.Now().Local()
+	cols := []string{"Version", "Content", "UpdateDate", "ModifyTime"}
+	if e := item.Update(cols); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "编辑更新日志失败, Err: " + e.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// Del
+// @Title 删除
+// @Description 删除
+// @Param	request	body models.EtaVersionUpdateLogDelReq true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /del [post]
+func (this *EtaVersionUpdateLogController) Del() {
+	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.EtaVersionUpdateLogDelReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	if req.Id <= 0 {
+		br.Msg = "参数有误"
+		return
+	}
+
+	ob := new(models.EtaVersionUpdateLog)
+	item, e := ob.GetItemById(req.Id)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "更新日志不存在, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取更新日志信息失败, Err: " + e.Error()
+		return
+	}
+	if e := item.Del(); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "删除更新日志失败, Err: " + e.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}

+ 539 - 0
controllers/help_doc/classify.go

@@ -0,0 +1,539 @@
+package help_doc
+
+import (
+	"encoding/json"
+	"eta/eta_forum_admin/controllers"
+	"eta/eta_forum_admin/models"
+	"eta/eta_forum_admin/models/help_doc"
+	"eta/eta_forum_admin/services"
+	"eta/eta_forum_admin/utils"
+	"time"
+)
+
+// HelpDocClassifyController 帮助文档分类
+type HelpDocClassifyController struct {
+	controllers.BaseAuthController
+}
+
+// ListClassify
+// @Title 获取分类列表
+// @Description 获取分类列表
+// @Param   KeyWord   query   string  true       "检索关键词"
+// @Success 200 {object} models.HelpDocClassifyListResp
+// @router /classify/list [get]
+func (this *HelpDocClassifyController) ListClassify() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	keyWord := this.GetString("KeyWord")
+
+	resp := new(help_doc.HelpDocClassifyListResp)
+
+	rootList, err := help_doc.GetHelpDocClassifyByParentId(0, "")
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+
+	docAll, err := help_doc.GetAllHelpDoc()
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+
+	classifyMap := make(map[int]int, 0)
+	for _, v := range docAll {
+		classifyMap[v.ClassifyId] = 1
+	}
+
+	classifyAll := make([]*help_doc.HelpDocClassifyItems, 0)
+	newClassifyAll := make([]*help_doc.HelpDocClassifyItems, 0)
+	newRootList := make([]help_doc.HelpDocClassifyItems, 0)
+	//parentMap := make(map[int]int)
+	classifyAllMap := make(map[int]*help_doc.HelpDocClassifyItems, 0)
+	classifyKeyMap := make(map[int]*help_doc.HelpDocClassifyItems, 0)
+	rootMap := make(map[*help_doc.HelpDocClassifyItems]int, 0)
+	if keyWord != "" {
+		classifyAll, err = help_doc.GetAllHelpDocClassifyByKeyword("")
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取数据失败,Err:" + err.Error()
+			return
+		}
+
+		for _, v := range classifyAll {
+			classifyAllMap[v.ClassifyId] = v
+		}
+
+		keywordClassify, err := help_doc.GetAllHelpDocClassifyByKeyword(keyWord)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取数据失败,Err:" + err.Error()
+			return
+		}
+		for _, v := range keywordClassify {
+			if v.Level == 3 {
+				ppid := classifyAllMap[v.ParentId].ParentId
+				//newRootList = append(newRootList, *classifyAllMap[ppid])
+				rootMap[classifyAllMap[ppid]] = 1
+				classifyKeyMap[v.ClassifyId] = v
+				classifyKeyMap[v.ParentId] = classifyAllMap[v.ParentId]
+			} else if v.Level == 2 {
+				pid := classifyAllMap[v.ParentId].ClassifyId
+				//newRootList = append(newRootList, *classifyAllMap[pid])
+				rootMap[classifyAllMap[pid]] = 1
+				classifyKeyMap[v.ClassifyId] = v
+			} else if v.Level == 1 {
+				//newRootList = append(newRootList, *v)
+				rootMap[v] = 1
+			}
+		}
+		// 重新组合 newRootList
+		//for _, v := range rootList {
+		//	if root,ok := rootMap[v.ClassifyId]; ok{
+		//		newRootList = append(newRootList, *root)
+		//	}
+		//}
+		for v, _ := range rootMap {
+			newRootList = append(newRootList, *v)
+		}
+		// 重新组合 newClassifyAll
+		for _, v := range classifyKeyMap {
+			if v.Level != 1 {
+				newClassifyAll = append(newClassifyAll, v)
+			}
+		}
+	} else {
+		classifyAll, err = help_doc.GetHelpDocClassifyAll("")
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取数据失败,Err:" + err.Error()
+			return
+		}
+		newRootList = rootList
+		newClassifyAll = classifyAll
+	}
+
+	for k := range newClassifyAll {
+		_, ok := classifyMap[newClassifyAll[k].ClassifyId]
+		newClassifyAll[k].Disabled = ok
+	}
+
+	//rootListAll := make([]help_doc.HelpDocClassifyItems, 0)
+	//rootListAll = rootList
+	nodeAll := make([]*help_doc.HelpDocClassifyItems, 0)
+	for k := range newRootList {
+		rootNode := newRootList[k]
+		services.HelpDocClassifyItemsMakeTree(newClassifyAll, &rootNode)
+		nodeAll = append(nodeAll, &rootNode)
+	}
+
+	//添加分类用,只有一二级目录
+	classifyLevel, err := help_doc.GetHelpDocClassifyLevel()
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+	for k := range classifyLevel {
+		_, ok := classifyMap[classifyLevel[k].ClassifyId]
+		classifyLevel[k].Disabled = ok
+	}
+
+	//rootListLevel := make([]help_doc.HelpDocClassifyItems, 0)
+	//rootListLevel = rootList
+	nodeAllTwoLevel := make([]*help_doc.HelpDocClassifyItems, 0)
+	for k := range rootList {
+		rootNode := rootList[k]
+		services.HelpDocClassifyItemsMakeTree(classifyLevel, &rootNode)
+		nodeAllTwoLevel = append(nodeAllTwoLevel, &rootNode)
+	}
+
+	resp.AllNodes = nodeAll
+	resp.TwoLevelNodes = nodeAllTwoLevel
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// @Title 新增分类
+// @Description 新增分类接口
+// @Param   ClassifyName   int  true       "分类名称"
+// @Param   ParentId   query   int  true       "父级Id 添加父级时为0"
+// @Param   Sort   query   string  false       "排序"
+// @Success 200 新增成功
+// @router /classify/add [post]
+func (this *HelpDocClassifyController) AddClassify() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req help_doc.AddHelpDocClassifyReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.HelpDocClassifyName == "" {
+		br.Msg = "请输入分类名称"
+		br.IsSendEmail = false
+		return
+	}
+	if req.ParentId < 0 {
+		br.Msg = "参数错误"
+		br.IsSendEmail = false
+		return
+	}
+
+	count, err := help_doc.GetHelpDocClassifyCount(req.HelpDocClassifyName, req.ParentId)
+	if err != nil {
+		br.Msg = "判断名称是否已存在失败"
+		br.ErrMsg = "判断名称是否已存在失败,Err:" + err.Error()
+		return
+	}
+	if count > 0 {
+		br.Msg = "分类名称已存在,请重新输入"
+		br.IsSendEmail = false
+		return
+	}
+	//获取该层级下最大的排序数
+	maxSort, err := help_doc.GetHelpDocClassifyMaxSort(req.ParentId)
+
+	classify := new(help_doc.HelpDocClassify)
+	classify.ParentId = req.ParentId
+	classify.ClassifyName = req.HelpDocClassifyName
+	classify.CreateTime = time.Now()
+	classify.ModifyTime = time.Now()
+	classify.SysUserId = this.SysUser.AdminId
+	classify.SysUserRealName = this.SysUser.RealName
+	classify.Level = req.Level + 1
+	classify.Sort = maxSort + 1
+
+	_, err = help_doc.AddHelpDocClassify(classify)
+	if err != nil {
+		br.Msg = "保存分类失败"
+		br.ErrMsg = "保存分类失败,Err:" + err.Error()
+		return
+	}
+	br.Ret = 200
+	br.Msg = "保存成功"
+	br.Success = true
+}
+
+// @Title 编辑分类
+// @Description 编辑分类接口
+// @Param   ClassifyId   int  true       "分类Id"
+// @Param   ClassifyName   string  true       "分类名称"
+// @Param   ParentId   query   int  true       "父级Id 添加父级时为0"
+// @Param   Sort   query   string  false       "排序"
+// @Success 200 保存成功
+// @router /classify/edit [post]
+func (this *HelpDocClassifyController) EditClassify() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req help_doc.EditHelpDocClassifyReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.HelpDocClassifyName == "" {
+		br.Msg = "请输入分类名称"
+		br.IsSendEmail = false
+		return
+	}
+
+	if req.HelpDocClassifyId <= 0 {
+		br.Msg = "参数错误"
+		br.IsSendEmail = false
+		return
+	}
+
+	item, err := help_doc.GetHelpDocClassifyById(req.HelpDocClassifyId)
+	if err != nil {
+		br.Msg = "保存失败"
+		br.Msg = "获取分类信息失败,Err:" + err.Error()
+		return
+	}
+
+	if item.ClassifyName != req.HelpDocClassifyName {
+		count, err := help_doc.GetHelpDocClassifyCount(req.HelpDocClassifyName, item.ParentId)
+		if err != nil {
+			br.Msg = "判断名称是否已存在失败"
+			br.ErrMsg = "判断名称是否已存在失败,Err:" + err.Error()
+			return
+		}
+		if count > 0 {
+			br.Msg = "分类名称已存在,请重新输入"
+			br.IsSendEmail = false
+			return
+		}
+
+		err = help_doc.EditHelpDocClassify(req.HelpDocClassifyId, req.HelpDocClassifyName)
+		if err != nil {
+			br.Msg = "保存失败"
+			br.ErrMsg = "保存失败,Err:" + err.Error()
+			return
+		}
+	}
+	br.Ret = 200
+	br.Msg = "保存成功"
+	br.Success = true
+	br.IsAddLog = true
+}
+
+// @Title 删除分类列表
+// @Description 删除分类列表
+// @Param   ClassifyId   int  true       "分类名称"
+// @Success 200 删除成功
+// @router /classify/delete [get]
+func (this *HelpDocClassifyController) DelClassify() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	classifyId, _ := this.GetInt("ClassifyId")
+
+	_, err := help_doc.GetHelpDocClassifyById(classifyId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			br.Msg = "当前分类不存在"
+			br.ErrMsg = "当前分类不存在"
+			return
+		}
+		br.Msg = "获取分类信息失败"
+		br.ErrMsg = "获取分类信息失败,Err:" + err.Error()
+		return
+	}
+
+	count, err := help_doc.GetHelpDocClassifyChildCounts(classifyId)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取信息失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+
+	if count > 0 {
+		br.Msg = "请先删除该分类下关联分类"
+		br.Ret = 403
+		return
+	}
+
+	reportCount, e := help_doc.GetHelpDocCounts(classifyId)
+	if e != nil && e.Error() != utils.ErrNoRow() {
+		br.Msg = "获取信息失败"
+		br.ErrMsg = "获取失败,Err:" + e.Error()
+		return
+	}
+
+	if reportCount > 0 {
+		br.Msg = "该分类已关联文章,不允许删除!"
+		br.Ret = 403
+		return
+	}
+
+	if err = help_doc.DeleteHelpDocClassify(classifyId); err != nil {
+		br.Msg = "删除失败"
+		br.ErrMsg = "删除分类失败, Err: " + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "删除成功"
+}
+
+// ChartClassifyMove
+// @Title 分类移动接口
+// @Description 分类移动接口
+// @Success 200 {object} data_manage.MoveChartClassifyReq
+// @router /classify/move [post]
+func (this *HelpDocClassifyController) ClassifyMove() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		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 help_doc.MoveClassifyReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	if req.ClassifyId <= 0 {
+		br.Msg = "参数错误"
+		br.ErrMsg = "分类id小于等于0"
+		return
+	}
+	//判断分类是否存在
+	classifyInfo, err := help_doc.GetHelpDocClassifyById(req.ClassifyId)
+	if err != nil {
+		br.Msg = "移动失败"
+		br.ErrMsg = "获取分类信息失败,Err:" + err.Error()
+		return
+	}
+	updateCol := make([]string, 0)
+
+	//判断上级id是否一致,如果不一致的话,那么需要移动该分类层级
+	if classifyInfo.ParentId != req.ParentClassifyId && req.ParentClassifyId != 0 {
+		parentClassifyInfo, err := help_doc.GetHelpDocClassifyById(req.ParentClassifyId)
+		if err != nil {
+			br.Msg = "移动失败"
+			br.ErrMsg = "获取上级分类信息失败,Err:" + err.Error()
+			return
+		}
+		classifyInfo.ParentId = parentClassifyInfo.ClassifyId
+		classifyInfo.Level = parentClassifyInfo.Level + 1
+		classifyInfo.ModifyTime = time.Now()
+		updateCol = append(updateCol, "ParentId", "Level", "ModifyTime")
+	}
+
+	//如果有传入 上一个兄弟节点分类id
+	if req.PrevClassifyId > 0 {
+		//上一个兄弟节点
+		prevClassify, err := help_doc.GetHelpDocClassifyById(req.PrevClassifyId)
+		if err != nil {
+			br.Msg = "移动失败"
+			br.ErrMsg = "获取上一个兄弟节点分类信息失败,Err:" + err.Error()
+			return
+		}
+
+		//如果是移动在两个兄弟节点之间
+		if req.NextClassifyId > 0 {
+			//下一个兄弟节点
+			nextClassify, err := help_doc.GetHelpDocClassifyById(req.NextClassifyId)
+			if err != nil {
+				br.Msg = "移动失败"
+				br.ErrMsg = "获取下一个兄弟节点分类信息失败,Err:" + err.Error()
+				return
+			}
+			//如果上一个兄弟与下一个兄弟的排序权重是一致的,那么需要将下一个兄弟(以及下个兄弟的同样排序权重)的排序权重+2,自己变成上一个兄弟的排序权重+1
+			if prevClassify.Sort == nextClassify.Sort || prevClassify.Sort == classifyInfo.Sort {
+				//变更兄弟节点的排序
+				updateSortStr := `sort + 2`
+				_ = help_doc.UpdateHelpDocClassifySortByParentId(prevClassify.ParentId, prevClassify.ClassifyId, prevClassify.Sort, updateSortStr)
+			} else {
+				//如果下一个兄弟的排序权重正好是上个兄弟节点的下一层,那么需要再加一层了
+				if nextClassify.Sort-prevClassify.Sort == 1 {
+					//变更兄弟节点的排序
+					updateSortStr := `sort + 1`
+					_ = help_doc.UpdateHelpDocClassifySortByParentId(prevClassify.ParentId, 0, prevClassify.Sort, updateSortStr)
+				}
+			}
+		}
+
+		classifyInfo.Sort = prevClassify.Sort + 1
+		classifyInfo.ModifyTime = time.Now()
+		updateCol = append(updateCol, "Sort", "ModifyTime")
+
+	} else {
+		firstClassify, err := help_doc.GetFirstHelpDocClassifyByParentId(classifyInfo.ParentId)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			br.Msg = "移动失败"
+			br.ErrMsg = "获取获取当前父级分类下的排序第一条的分类信息失败,Err:" + err.Error()
+			return
+		}
+
+		//如果该分类下存在其他分类,且第一个其他分类的排序等于0,那么需要调整排序
+		if firstClassify != nil && firstClassify.Sort == 0 {
+			updateSortStr := ` sort + 1 `
+			_ = help_doc.UpdateHelpDocClassifySortByParentId(firstClassify.ParentId, firstClassify.ClassifyId-1, 0, updateSortStr)
+		}
+
+		classifyInfo.Sort = 0 //那就是排在第一位
+		classifyInfo.ModifyTime = time.Now()
+		updateCol = append(updateCol, "Sort", "ModifyTime")
+	}
+
+	//更新
+	if len(updateCol) > 0 {
+		err = classifyInfo.Update(updateCol)
+		if err != nil {
+			br.Msg = "移动失败"
+			br.ErrMsg = "修改失败,Err:" + err.Error()
+			return
+		}
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "移动成功"
+}
+
+// @Title 编辑分类可见权限
+// @Description 编辑分类可见权限接口
+// @Param   ClassifyId   int  true       "分类Id"
+// @Param   ClassifyName   string  true       "分类名称"
+// @Param   ParentId   query   int  true       "父级Id 添加父级时为0"
+// @Param   Sort   query   string  false       "排序"
+// @Success 200 保存成功
+// @router /classify/visible/edit [post]
+func (this *HelpDocClassifyController) EditClassifyVisible() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req help_doc.EditHelpDocClassifyVisibleReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	if req.HelpDocClassifyId <= 0 {
+		br.Msg = "参数错误"
+		br.IsSendEmail = false
+		return
+	}
+
+	//item, err := help_doc.GetHelpDocClassifyById(req.HelpDocClassifyId)
+	//if err != nil {
+	//	br.Msg = "保存失败"
+	//	br.Msg = "获取分类信息失败,Err:" + err.Error()
+	//	return
+	//}
+
+	err = help_doc.EditHelpDocClassifyVisible(req.HelpDocClassifyId, req.VisibleBusinessIds)
+	if err != nil {
+		br.Msg = "保存失败"
+		br.ErrMsg = "保存失败,Err:" + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Msg = "保存成功"
+	br.Success = true
+	br.IsAddLog = true
+}

+ 448 - 0
controllers/help_doc/doc.go

@@ -0,0 +1,448 @@
+package help_doc
+
+import (
+	"encoding/json"
+	"eta/eta_forum_admin/controllers"
+	"eta/eta_forum_admin/models"
+	"eta/eta_forum_admin/models/help_doc"
+	"eta/eta_forum_admin/utils"
+	"fmt"
+	"html"
+	"strconv"
+	"strings"
+	"time"
+
+	"github.com/rdlucklib/rdluck_tools/paging"
+)
+
+// HelpDocController 帮助文档
+type HelpDocController struct {
+	controllers.BaseAuthController
+}
+
+// @Title 新增报告接口
+// @Description 新增报告
+// @Param	request	body help_doc.AddHelpDocReq true "type json string"
+// @Success 200 {object} models.AddEnglishReportResp
+// @router /add [post]
+func (this *HelpDocController) Add() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		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 help_doc.AddHelpDocReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	fmt.Println("Id:", req.Id)
+	if req.Content == "" {
+		br.Msg = "请输入内容"
+		return
+	}
+	var anchor []byte
+	if req.AnchorData != nil {
+		anchor, err = json.Marshal(req.AnchorData)
+		if err != nil {
+			br.Msg = "参数解析异常!"
+			br.ErrMsg = "参数解析失败,Err:" + err.Error()
+			return
+		}
+	}
+	var recommend []byte
+	if req.RecommendData != nil {
+		recommend, err = json.Marshal(req.RecommendData)
+		if err != nil {
+			br.Msg = "参数解析异常!"
+			br.ErrMsg = "参数解析失败,Err:" + err.Error()
+			return
+		}
+	}
+
+	var resp help_doc.HelpDocSaveResp
+	resp.HelpDocId = req.Id
+	resp.ModifyTime = time.Now().Format(utils.FormatDateTime)
+
+	item := new(help_doc.HelpDoc)
+	item.ClassifyId = req.ClassifyId
+	item.Title = req.Title
+	item.Author = req.Author
+	item.Status = req.Status
+	item.Content = html.EscapeString(req.Content)
+	item.CreateTime = time.Now()
+	item.ModifyTime = time.Now()
+	item.AdminId = sysUser.AdminId
+	item.AdminRealName = sysUser.RealName
+	item.Anchor = string(anchor)
+	item.Recommend = string(recommend)
+	if req.Status == 2 {
+		item.PublishTime = time.Now()
+	}
+	if req.Id > 0 {
+		tmpHelpDoc, err := help_doc.GetHelpDocById(int(req.Id))
+		if err != nil {
+			br.Msg = "保存失败"
+			br.ErrMsg = "查询帮助文档失败,Err:" + err.Error()
+			return
+		}
+		if tmpHelpDoc.Status != req.Status {
+			req.IsChange = true
+		}
+	}
+
+	if req.Id == 0 {
+		id, e := help_doc.AddHelpDoc(item)
+		if e != nil {
+			err = e
+			br.Msg = "新增失败"
+			br.ErrMsg = "保存失败,Err:" + err.Error()
+			return
+		}
+		resp.HelpDocId = id
+		resp.ModifyTime = item.ModifyTime.Format(utils.FormatDateTime)
+	} else if req.IsChange {
+		err = help_doc.EditHelpDoc(item, req.Id)
+		if err != nil {
+			br.Msg = "保存失败"
+			br.ErrMsg = "保存失败,Err:" + err.Error()
+			return
+		}
+	}
+
+	if req.Status == 1 {
+		br.Msg = "保存成功"
+	} else {
+		br.Msg = "发布成功"
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Data = resp
+}
+
+// @Title 获取报告详情接口
+// @Description 获取报告详情
+// @Param	request	body models.ReportDetailReq true "type json string"
+// @Success 200 {object} models.EnglishReportDetailView
+// @router /detail [get]
+func (this *HelpDocController) Detail() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	docId, err := this.GetInt("DocId")
+	if err != nil {
+		br.Msg = "获取参数失败!"
+		br.ErrMsg = "获取参数失败,Err:" + err.Error()
+		return
+	}
+	if docId <= 0 {
+		br.Msg = "参数错误"
+		return
+	}
+	item, err := help_doc.GetHelpDocById(docId)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+
+	item.Content = html.UnescapeString(item.Content)
+
+	var anchor []help_doc.AnchorList
+	if item.Anchor != "" {
+		err = json.Unmarshal([]byte(item.Anchor), &anchor)
+		if err != nil {
+			br.Msg = "解析失败"
+			br.ErrMsg = "解析失败,Err:" + err.Error()
+			return
+		}
+	}
+
+	var recommend []help_doc.RecommendList
+	if item.Recommend != "" {
+		err = json.Unmarshal([]byte(item.Recommend), &recommend)
+		if err != nil {
+			br.Msg = "解析失败"
+			br.ErrMsg = "解析失败,Err:" + err.Error()
+			return
+		}
+	}
+
+	resp := help_doc.HelpDocResp{
+		Id:            item.Id,
+		ClassifyId:    item.ClassifyId,
+		Title:         item.Title,
+		Author:        item.Author,
+		CreateTime:    item.CreateTime,
+		ModifyTime:    item.ModifyTime,
+		Status:        item.Status,
+		PublishTime:   item.PublishTime,
+		Content:       item.Content,
+		AdminId:       item.AdminId,
+		AdminRealName: item.AdminRealName,
+		Anchor:        anchor,
+		Recommend:     recommend,
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// @Title 获取报告列表接口
+// @Description 获取报告列表
+// @Param   PageSize   query   int  true       "每页数据条数"
+// @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
+// @Param   KeyWord   query   string  true       "搜索关键词"
+// @Success 200 {object} models.ReportListResp
+// @router /list [get]
+func (this *HelpDocController) ListReport() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		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")
+
+	classifyIds := this.GetString("ClassifyIds")
+	keyWord := this.GetString("KeyWord")
+
+	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 keyWord != "" {
+		condition += ` AND title LIKE '%` + keyWord + `%' `
+	}
+
+	if len(classifyIds) > 0 {
+		classifyIdSlice := strings.Split(classifyIds, ",")
+		condition += ` AND classify_id IN ( ` + utils.GetOrmInReplace(len(classifyIdSlice)) + ` ) `
+		pars = append(pars, classifyIdSlice)
+	}
+
+	//拿到所有分类信息,显示路径用
+	classifyAll, err := help_doc.GetAllHelpDocClassify()
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+
+	parentMap := make(map[int]int)
+	classifyNameMap := make(map[int]string)
+	for _, v := range classifyAll {
+		classifyNameMap[v.ClassifyId] = v.ClassifyName
+		if v.ParentId != 0 {
+			parentMap[v.ClassifyId] = v.ParentId
+		}
+	}
+
+	total, err := help_doc.GetHelpDocListCount(condition, pars)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+	list, err := help_doc.GetHelpDocList(condition, pars, startSize, pageSize)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+
+	resp := new(help_doc.HelpDocListResp)
+	for _, item := range list {
+		var anchor []help_doc.AnchorList
+		if item.Anchor != "" {
+			err = json.Unmarshal([]byte(item.Anchor), &anchor)
+			if err != nil {
+				br.Msg = "解析失败"
+				br.ErrMsg = "解析失败,Err:" + err.Error()
+				return
+			}
+		}
+
+		var recommend []help_doc.RecommendList
+		if item.Recommend != "" {
+			err = json.Unmarshal([]byte(item.Recommend), &recommend)
+			if err != nil {
+				br.Msg = "解析失败"
+				br.ErrMsg = "解析失败,Err:" + err.Error()
+				return
+			}
+		}
+		respItem := help_doc.HelpDocResp{
+			Id:            item.Id,
+			ClassifyId:    item.ClassifyId,
+			Title:         item.Title,
+			Author:        item.Author,
+			CreateTime:    item.CreateTime,
+			ModifyTime:    item.ModifyTime,
+			Status:        item.Status,
+			PublishTime:   item.PublishTime,
+			Content:       item.Content,
+			AdminId:       item.AdminId,
+			AdminRealName: item.AdminRealName,
+			Anchor:        anchor,
+			Recommend:     recommend,
+		}
+		if pid, ok := parentMap[respItem.ClassifyId]; ok {
+			pName := classifyNameMap[pid]
+			if ppid, ok := parentMap[pid]; ok {
+				ppName := classifyNameMap[ppid]
+				respItem.ClassifyName = ppName + "/" + pName + "/" + classifyNameMap[respItem.ClassifyId]
+			} else {
+				respItem.ClassifyName = pName + "/" + classifyNameMap[respItem.ClassifyId]
+			}
+		} else {
+			respItem.ClassifyName = classifyNameMap[respItem.ClassifyId]
+		}
+		resp.List = append(resp.List, &respItem)
+	}
+
+	page := paging.GetPaging(currentIndex, pageSize, total)
+	resp.Paging = page
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// @Title 发布报告接口
+// @Description 发布报告
+// @Param	request	body models.PublishReq true "type json string"
+// @Success 200 Ret=200 发布成功
+// @router /publish [post]
+func (this *HelpDocController) PublishReport() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req help_doc.PublishReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	docId := req.DocId
+	if docId <= 0 {
+		br.Msg = "参数错误"
+		br.ErrMsg = "参数错误,报告id不可为空"
+		return
+	}
+
+	doc, err := help_doc.GetHelpDocById(docId)
+	if err != nil {
+		br.Msg = "获取报告信息失败"
+		br.ErrMsg = "获取报告信息失败,Err:" + err.Error()
+		return
+	}
+	if doc == nil {
+		br.Msg = "报告不存在"
+		return
+	}
+
+	var tmpErr error
+
+	if doc.Content == "" {
+		br.Msg = "报告内容为空,不可发布"
+		br.ErrMsg = "报告内容为空,不需要生成,report_id:" + strconv.Itoa(doc.Id)
+		return
+	}
+	if tmpErr = help_doc.PublishHelpDocById(docId, req.Status); tmpErr != nil {
+		br.Msg = "报告发布失败"
+		br.ErrMsg = "报告发布失败, Err:" + tmpErr.Error() + ", doc_id:" + strconv.Itoa(docId)
+		return
+	}
+
+	if req.Status == 1 {
+		br.Msg = "取消发布成功"
+	} else {
+		br.Msg = "发布成功"
+	}
+
+	br.Ret = 200
+	br.Success = true
+
+}
+
+// @Title 删除报告接口
+// @Description 删除报告
+// @Param	request	body models.DeleteReq true "type json string"
+// @Success 200 Ret=200 删除成功
+// @router /delete [post]
+func (this *HelpDocController) Delete() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req help_doc.DeleteReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.DocId <= 0 {
+		br.Msg = "参数错误"
+		br.ErrMsg = "参数错误,报告id不可为空"
+		return
+	}
+	_, err = help_doc.GetHelpDocById(req.DocId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			br.Msg = "报告不存在"
+			return
+		}
+		br.Msg = "查询报告信息失败"
+		br.ErrMsg = "查询报告信息失败, Err" + err.Error()
+		return
+	}
+
+	err = help_doc.DeleteHelpDoc(req.DocId)
+	if err != nil {
+		br.Msg = "删除报告失败"
+		br.ErrMsg = "删除报告失败, Err" + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "删除成功"
+}

+ 167 - 0
controllers/resource.go

@@ -0,0 +1,167 @@
+package controllers
+
+import (
+	"eta/eta_forum_admin/models"
+	"eta/eta_forum_admin/models/system"
+	"eta/eta_forum_admin/services"
+	"eta/eta_forum_admin/utils"
+	"github.com/h2non/filetype"
+	"io/ioutil"
+	"os"
+	"path"
+	"time"
+)
+
+type ResourceController struct {
+	BaseAuthController
+}
+
+// @Title 图片上传
+// @Description 图片上传接口
+// @Param   File   query   file  true       "文件"
+// @Success 200 新增成功
+// @router /image/upload [post]
+func (this *ResourceController) ImageUpload() {
+	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
+	}
+
+	f, h, err := this.GetFile("file")
+	if err != nil {
+		br.Msg = "获取资源信息失败"
+		br.ErrMsg = "获取资源信息失败,Err:" + err.Error()
+		return
+	}
+	defer f.Close() //关闭上传文件
+
+	// 不依赖于文件扩展名检查文件格式
+	fileData, e := ioutil.ReadAll(f)
+	if e != nil {
+		br.Msg = "上传失败"
+		br.ErrMsg = "读取文件失败, Err: " + e.Error()
+		return
+	}
+	pass := filetype.IsImage(fileData)
+	if !pass {
+		br.Msg = "文件格式有误"
+		br.ErrMsg = "文件格式有误"
+		return
+	}
+
+	ext := path.Ext(h.Filename)
+	dateDir := time.Now().Format("20060102")
+	uploadDir := utils.STATIC_DIR + "image/" + dateDir
+	err = os.MkdirAll(uploadDir, utils.DIR_MOD)
+	if err != nil {
+		br.Msg = "存储目录创建失败"
+		br.ErrMsg = "存储目录创建失败,Err:" + err.Error()
+		return
+	}
+	randStr := utils.GetRandStringNoSpecialChar(28)
+	fileName := randStr + ext
+	fpath := uploadDir + "/" + fileName
+	err = this.SaveToFile("file", fpath)
+	if err != nil {
+		br.Msg = "文件上传失败"
+		br.ErrMsg = "文件上传失败,Err:" + err.Error()
+		return
+	}
+	defer func() {
+		os.Remove(fpath)
+	}()
+
+	// 上传文件
+	resourceUrl := ``
+	ossClient := services.NewOssClient()
+	if ossClient == nil {
+		br.Msg = "上传失败"
+		br.ErrMsg = "初始化OSS服务失败"
+		return
+	}
+	// 上传到阿里云
+	ossDir := utils.UploadDir + "images/"
+	savePath := ossDir + time.Now().Format("200601/20060102/") + fileName
+	resourceUrl, err = ossClient.UploadFile(fileName, fpath, savePath)
+	if err != nil {
+		br.Msg = "文件上传失败"
+		br.ErrMsg = "文件上传失败,Err:" + err.Error()
+		return
+	}
+
+	resp := new(system.ResourceResp)
+	resp.ResourceUrl = resourceUrl
+
+	br.Msg = "上传成功"
+	br.Ret = 200
+	br.Success = true
+	br.Data = resp
+	return
+}
+
+// @Title 获取STSToken
+// @Description 获取STSToken
+// @Success 200 获取成功
+// @router /oss/get_sts_token [get]
+func (this *ResourceController) OssSTSToken() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	ossClient := services.NewOssClient()
+	if ossClient == nil {
+		br.Msg = "上传失败"
+		br.ErrMsg = "初始化OSS服务失败"
+		return
+	}
+	resp, e := ossClient.GetUploadToken()
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取OSS上传Token失败, Err: " + e.Error()
+		return
+	}
+
+	br.Data = resp
+	br.Msg = "获取成功"
+	br.Ret = 200
+	br.Success = true
+
+	//source, _ := this.GetInt("StorageSource")
+	//
+	//if source == utils.STORAGESOURCE_OSS {
+	//	resp, err := services.GetOssSTSToken()
+	//	if err != nil {
+	//		br.Msg = "获取失败"
+	//		br.ErrMsg = "获取STSToken失败, Err: " + err.Error()
+	//		return
+	//	}
+	//	br.Data = resp
+	//	br.Msg = "获取成功"
+	//	br.Ret = 200
+	//	br.Success = true
+	//} else if source == utils.STORAGESOURCE_MINIO {
+	//	resp, err := services.GetMinIOSTSToken()
+	//	if err != nil {
+	//		br.Msg = "获取失败"
+	//		br.ErrMsg = "获取STSToken失败, Err: " + err.Error()
+	//		return
+	//	}
+	//	br.Data = resp
+	//	br.Msg = "获取成功"
+	//	br.Ret = 200
+	//	br.Success = true
+	//}
+}

+ 72 - 0
controllers/sys_menu.go

@@ -0,0 +1,72 @@
+package controllers
+
+import (
+	"eta/eta_forum_admin/models"
+	"eta/eta_forum_admin/models/system"
+	"strconv"
+)
+
+type SysMenuController struct {
+	BaseAuthController
+}
+
+// @Title 获取用户权限菜单
+// @Description 获取用户权限菜单接口
+// @Success 200 {object} system.MenuListResp
+// @router /menu/list [get]
+func (this *SysRoleController) SysMenuList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		return
+	}
+	roleId := sysUser.RoleId
+	//roleId=1
+	if roleId <= 0 {
+		br.Msg = "参数错误"
+		return
+	}
+	roleIds := strconv.Itoa(roleId)
+	//查询账号绑定的其他角色
+	otherRoles, err := system.GetRoleIdsByAdminId(sysUser.AdminId)
+	if err != nil {
+		br.Msg = "获取其他角色失败"
+		br.ErrMsg = "获取其他角色失败,Err:" + err.Error()
+		return
+	}
+	if len(otherRoles) > 0 {
+		for _, v := range otherRoles {
+			roleIds += "," + strconv.Itoa(v.RoleId)
+		}
+	}
+
+	list, err := system.GetMenuListByRoleIds(roleIds)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+	lenList := len(list)
+	for i := 0; i < lenList; i++ {
+		item := list[i]
+		child, err := system.GetMenuByParentIdRoleIds(roleIds, item.MenuId)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取子菜单数据失败,Err:" + err.Error()
+			return
+		}
+		list[i].Children = child
+	}
+	resp := new(system.MenuListResp)
+	resp.List = list
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}

+ 334 - 0
controllers/sys_role.go

@@ -0,0 +1,334 @@
+package controllers
+
+import (
+	"encoding/json"
+	"eta/eta_forum_admin/models"
+	"eta/eta_forum_admin/models/system"
+	"eta/eta_forum_admin/utils"
+	"fmt"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"strconv"
+	"strings"
+)
+
+type SysRoleController struct {
+	BaseAuthController
+}
+
+// @Title 获取角色列表
+// @Description 获取角色列表接口
+// @Param   PageSize   query   int  true       "每页数据条数"
+// @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
+// @Param   KeyWord   query   string  true       "搜索关键词"
+// @Success 200 {object} system.SysRoleListResp
+// @router /role/list [get]
+func (this *SysRoleController) ListSysRole() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		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")
+	keyWord := this.GetString("KeyWord")
+
+	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 keyWord != "" {
+		condition += ` AND role_name LIKE '%` + keyWord + `%'  `
+	}
+
+	total, err := system.GetSysRoleListCount(condition, pars)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+
+	list, err := system.GetSysRoleList(condition, pars, startSize, pageSize)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+	page := paging.GetPaging(currentIndex, pageSize, total)
+	resp := new(system.SysRoleListResp)
+	resp.List = list
+	resp.Paging = page
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// @Title 角色设置权限-菜单列表
+// @Description 角色设置权限-菜单列表
+// @Param   RoleId   query   int  true       "角色Id"
+// @Success 200 {object} system.SysRoleListResp
+// @router /role/menu/list [get]
+func (this *SysRoleController) SysRoleMenusList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	roleId, err := this.GetInt("RoleId")
+	if err != nil {
+		br.Msg = "获取角色失败"
+		br.ErrMsg = "获取角色失败,Err:" + err.Error()
+		return
+	}
+	fmt.Println("roleId", roleId)
+	list := make([]*system.RoleMenuList, 0)
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		return
+	}
+	list, err = system.GetRoleMenuList()
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+	lenList := len(list)
+	for i := 0; i < lenList; i++ {
+		item := list[i]
+		child, err := system.GetRoleMenuByParentId(item.MenuId)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取子菜单数据失败,Err:" + err.Error()
+			return
+		}
+		list[i].Child = child
+
+		checkList := make([]int, 0)
+		if roleId > 0 {
+			checkItem, err := system.GetCheckListRoleMenu(roleId, item.MenuId)
+			if err != nil {
+				br.Msg = "获取失败"
+				br.ErrMsg = "获取选择数据失败,Err:" + err.Error()
+				return
+			}
+			for _, v := range checkItem {
+				checkList = append(checkList, v.MenuId)
+			}
+		}
+		list[i].CheckList = checkList
+	}
+	resp := new(system.RoleMenuListResp)
+	resp.List = list
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// @Title 角色设置权限
+// @Description 角色设置权限
+// @Param	request	body system.RoleMenusAddReq true "type json string"
+// @Success 200 {object} system.SysRoleListResp
+// @router /role/menu/add [post]
+func (this *SysRoleController) SysRoleMenusAdd() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req system.RoleMenusAddReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	roleId := req.RoleId
+	if roleId <= 0 {
+		br.Msg = "参数错误"
+		br.ErrMsg = "参数错误,RoleId 小于等于0 "
+		return
+	}
+	if req.MenuIdStr == "" {
+		br.Msg = "请选择菜单"
+		br.ErrMsg = "请选择菜单,MenuIdStr IS Empty "
+		return
+	}
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		return
+	}
+	err = system.DeleteRoleMenuByRoleId(roleId)
+	if err != nil {
+		br.Msg = "设置失败"
+		br.ErrMsg = "清空原有设置失败,Err:" + err.Error()
+		return
+	}
+	menuArr := strings.Split(req.MenuIdStr, ",")
+	for _, v := range menuArr {
+		menuId, err := strconv.Atoi(v)
+		if err != nil {
+			br.Msg = "设置失败"
+			br.ErrMsg = "menuId 解析失败,Err:" + err.Error()
+			return
+		}
+		//item := new(system.SysRoleMenu)
+		item := new(system.SysRoleMenu)
+		item.RoleId = roleId
+		item.MenuId = menuId
+		_, err = system.AddSysRoleMenu(item)
+		if err != nil {
+			br.Msg = "设置失败"
+			br.ErrMsg = "新增设置失败,Err:" + err.Error()
+			return
+		}
+	}
+	/*if req.MenuButtonIdStr != "" {
+		err = system.DeleteSysRoleButton(roleId)
+		if err != nil {
+			br.Msg = "设置失败"
+			br.ErrMsg = "删除已设置数据失败,Err:" + err.Error()
+			return
+		}
+		buttonMap := make(map[string]string)
+		buttonArr := strings.Split(req.MenuButtonIdStr, ",")
+		for _, v := range buttonArr {
+			if _, ok := buttonMap[v]; !ok {
+				buttonId, err := strconv.Atoi(v)
+				count, err := system.GetSysRoleButtonCount(roleId, buttonId)
+				if err != nil {
+					br.Msg = "判断设置失败存在失败"
+					br.ErrMsg = "判断设置失败存在失败,Err:" + err.Error()
+					return
+				}
+				if count <= 0 {
+					button := new(system.SysRoleButton)
+					button.RoleId = roleId
+					button.MenuButtonId = buttonId
+					button.CreateTime = time.Now()
+					_, err = system.AddSysRoleButton(button)
+					if err != nil {
+						br.Msg = "设置失败"
+						br.ErrMsg = "按钮权限设置失败,Err:" + err.Error()
+						return
+					}
+				}
+			}
+			buttonMap[v] = v
+		}
+	} else {
+		err = system.DeleteSysRoleButton(roleId)
+		if err != nil {
+			br.Msg = "设置失败"
+			br.ErrMsg = "删除已设置数据失败,Err:" + err.Error()
+			return
+		}
+	}*/
+	br.Ret = 200
+	br.Success = true
+	br.IsAddLog = true
+	br.Msg = "设置成功"
+}
+
+// @Title 获取所有角色
+// @Description 获取所有角色
+// @Success 200 {object} system.SysRoleListResp
+// @router /role/all [get]
+func (this *SysRoleController) AllSysRole() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		return
+	}
+	levelFlag := false
+	roleLevel := this.GetString("RoleLevel")
+	if roleLevel != "" {
+		levelFlag = true
+	}
+	list, err := system.GetSysRoleAll(levelFlag)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+	resp := new(system.SysRoleAllResp)
+	resp.List = list
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// @Title 获取菜单全部按钮
+// @Description 获取菜单全部按钮接口
+// @Param   RoleId   query   int  true       "角色ID"
+// @Success 200 {object} system.MenuButtonsResp
+// @router /menu/buttons [get]
+func (this *SysRoleController) SysMenuButtons() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	list, err := system.GetButtonMenu()
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+	listLen := len(list)
+	for i := 0; i < listLen; i++ {
+		item := list[i]
+		menus, err := system.GetSysMenuButtonItems(item.MenuId)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取失败,Err:" + err.Error()
+			return
+		}
+		list[i].List = menus
+	}
+	roleId, _ := this.GetInt("RoleId")
+	checkList := make([]*int, 0)
+	cl, err := system.GetRoleButtonCheck(roleId)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+	for _, v := range cl {
+		checkList = append(checkList, &v.MenuButtonId)
+	}
+	resp := new(system.MenuButtonsResp)
+	resp.List = list
+	resp.CheckList = checkList
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}

+ 28 - 7
go.mod

@@ -3,16 +3,23 @@ module eta/eta_forum_admin
 go 1.21.7
 
 require (
+	github.com/aliyun/alibaba-cloud-sdk-go v1.62.800
+	github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible
+	github.com/aws/aws-sdk-go v1.43.21
 	github.com/beego/bee/v2 v2.1.0
 	github.com/beego/beego/v2 v2.1.0
 	github.com/dgrijalva/jwt-go v3.2.0+incompatible
 	github.com/go-redis/redis/v8 v8.11.5
 	github.com/go-sql-driver/mysql v1.7.0
 	github.com/gonum/stat v0.0.0-20181125101827-41a0da705a5b
+	github.com/h2non/filetype v1.1.3
+	github.com/minio/minio-go/v7 v7.0.74
+	github.com/mozillazg/go-pinyin v0.20.0
 	github.com/nosixtools/solarlunar v0.0.0-20211112060703-1b6dea7b4a19
 	github.com/olivere/elastic/v7 v7.0.32
 	github.com/rdlucklib/rdluck_tools v1.0.3
 	github.com/shopspring/decimal v1.3.1
+	github.com/tealeg/xlsx v1.0.5
 	go.mongodb.org/mongo-driver v1.15.0
 	gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
 )
@@ -21,6 +28,9 @@ require (
 	github.com/beorn7/perks v1.0.1 // indirect
 	github.com/cespare/xxhash/v2 v2.2.0 // indirect
 	github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
+	github.com/dustin/go-humanize v1.0.1 // indirect
+	github.com/go-ini/ini v1.67.0 // indirect
+	github.com/goccy/go-json v0.10.3 // indirect
 	github.com/golang/protobuf v1.5.3 // indirect
 	github.com/golang/snappy v0.0.1 // indirect
 	github.com/gonum/blas v0.0.0-20181208220705-f22b278b28ac // indirect
@@ -29,31 +39,42 @@ require (
 	github.com/gonum/internal v0.0.0-20181124074243-f884aa714029 // indirect
 	github.com/gonum/lapack v0.0.0-20181123203213-e4cdc5a0bff9 // indirect
 	github.com/gonum/matrix v0.0.0-20181209220409-c518dec07be9 // indirect
+	github.com/google/uuid v1.6.0 // indirect
 	github.com/hashicorp/golang-lru v0.5.4 // indirect
+	github.com/jmespath/go-jmespath v0.4.0 // indirect
 	github.com/josharian/intern v1.0.0 // indirect
-	github.com/klauspost/compress v1.13.6 // indirect
+	github.com/json-iterator/go v1.1.12 // indirect
+	github.com/klauspost/compress v1.17.9 // indirect
+	github.com/klauspost/cpuid/v2 v2.2.8 // indirect
 	github.com/kr/text v0.2.0 // indirect
 	github.com/mailru/easyjson v0.7.7 // indirect
 	github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
+	github.com/minio/md5-simd v1.1.2 // indirect
 	github.com/mitchellh/mapstructure v1.5.0 // indirect
+	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+	github.com/modern-go/reflect2 v1.0.2 // indirect
 	github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect
+	github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
 	github.com/pkg/errors v0.9.1 // indirect
 	github.com/prometheus/client_golang v1.15.1 // indirect
 	github.com/prometheus/client_model v0.3.0 // indirect
 	github.com/prometheus/common v0.42.0 // indirect
 	github.com/prometheus/procfs v0.9.0 // indirect
+	github.com/rs/xid v1.5.0 // indirect
 	github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18 // indirect
-	github.com/stretchr/testify v1.8.4 // indirect
+	github.com/stretchr/testify v1.9.0 // indirect
 	github.com/xdg-go/pbkdf2 v1.0.0 // indirect
 	github.com/xdg-go/scram v1.1.2 // indirect
 	github.com/xdg-go/stringprep v1.0.4 // indirect
 	github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
-	golang.org/x/crypto v0.19.0 // indirect
-	golang.org/x/net v0.21.0 // indirect
-	golang.org/x/sync v0.1.0 // indirect
-	golang.org/x/sys v0.17.0 // indirect
-	golang.org/x/text v0.14.0 // indirect
+	golang.org/x/crypto v0.24.0 // indirect
+	golang.org/x/net v0.26.0 // indirect
+	golang.org/x/sync v0.7.0 // indirect
+	golang.org/x/sys v0.21.0 // indirect
+	golang.org/x/text v0.16.0 // indirect
+	golang.org/x/time v0.8.0 // indirect
 	google.golang.org/protobuf v1.30.0 // indirect
 	gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
+	gopkg.in/ini.v1 v1.67.0 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 )

+ 96 - 0
go.sum

@@ -1,12 +1,22 @@
+dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
+github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=
 github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
+github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
 github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
 github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
 github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
 github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
 github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
 github.com/alicebob/miniredis v2.5.0+incompatible/go.mod h1:8HZjEj4yU0dwhYHky+DxYx+6BMjkBbe5ONFIF1MXffk=
+github.com/aliyun/alibaba-cloud-sdk-go v1.62.800 h1:1NucX8xUg3F6m8Z6ermSniqyNXvUsfaW7B6CRMTpc0s=
+github.com/aliyun/alibaba-cloud-sdk-go v1.62.800/go.mod h1:SOSDHfe1kX91v3W5QiBsWSLqeLxImobbMX1mxrFHsVQ=
+github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible h1:8psS8a+wKfiLt1iVDX79F7Y6wUM49Lcha2FMXt4UM8g=
+github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
 github.com/astaxie/beego v1.12.3/go.mod h1:p3qIm0Ryx7zeBHLljmd7omloyca1s4yu1a8kM1FkpIA=
+github.com/aws/aws-sdk-go v1.43.21 h1:E4S2eX3d2gKJyI/ISrcIrSwXwqjIvCK85gtBMt4sAPE=
+github.com/aws/aws-sdk-go v1.43.21/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
 github.com/beego/bee/v2 v2.1.0 h1:4WngbAnkvVOyKy74WXcRH3clon76wkjhuzrV2mx2fQU=
 github.com/beego/bee/v2 v2.1.0/go.mod h1:wDhKy5TNxv46LHKsK2gyxo38ObCOm9PbCN89lWHK3EU=
 github.com/beego/beego/v2 v2.1.0 h1:Lk0FtQGvDQCx5V5yEu4XwDsIgt+QOlNjt5emUa3/ZmA=
@@ -35,11 +45,14 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumC
 github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
 github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
 github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
+github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
+github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
 github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
 github.com/elastic/go-elasticsearch/v6 v6.8.5/go.mod h1:UwaDJsD3rWLM5rKNFzv9hgox93HoX8utj1kxD9aFUcI=
 github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
 github.com/elazarl/go-bindata-assetfs v1.0.1 h1:m0kkaHRKEu7tUIUFVwhGGGYClXvyl4RE03qmvRTNfbw=
 github.com/elazarl/go-bindata-assetfs v1.0.1/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
+github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
 github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
 github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
@@ -47,6 +60,9 @@ github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWo
 github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
 github.com/garyburd/redigo v1.6.3/go.mod h1:rTb6epsqigu3kYKBnaF028A7Tf/Aw5s0cqA47doKKqw=
 github.com/glendc/gopher-json v0.0.0-20170414221815-dc4743023d0c/go.mod h1:Gja1A+xZ9BoviGJNA2E9vFkPjjsl+CoJxSXiQM1UXtw=
+github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
+github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
+github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
 github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
@@ -59,7 +75,11 @@ github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LB
 github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
 github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
+github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
 github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
+github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
 github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -95,20 +115,36 @@ github.com/gonum/stat v0.0.0-20181125101827-41a0da705a5b/go.mod h1:Z4GIJBJO3Wa4g
 github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
 github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/h2non/filetype v1.1.3 h1:FKkx9QbD7HR/zjK1Ia5XiBsq9zdLi5Kf3zGyFTAFkGg=
+github.com/h2non/filetype v1.1.3/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY=
 github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
 github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
 github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
+github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
+github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
+github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
 github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
 github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
 github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
 github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
+github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
 github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
+github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
 github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
 github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
+github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
+github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
+github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
+github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=
+github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
 github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
@@ -130,14 +166,23 @@ github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW
 github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
 github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
 github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
+github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
+github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
+github.com/minio/minio-go/v7 v7.0.74 h1:fTo/XlPBTSpo3BAMshlwKL5RspXRv9us5UeHEGYCFe0=
+github.com/minio/minio-go/v7 v7.0.74/go.mod h1:qydcVzV8Hqtj1VtEocfxbmVFa2siu6HGa+LDEPogjD8=
 github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
 github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
 github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
+github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
 github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0=
 github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
+github.com/mozillazg/go-pinyin v0.20.0 h1:BtR3DsxpApHfKReaPO1fCqF4pThRwH9uwvXzm+GnMFQ=
+github.com/mozillazg/go-pinyin v0.20.0/go.mod h1:iR4EnMMRXkfpFVV5FMi4FNB6wGq9NV6uDWbUuPhP4Yc=
 github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
 github.com/nosixtools/solarlunar v0.0.0-20211112060703-1b6dea7b4a19 h1:LhWT2dBuNkYexwRSsPpYh67e0ikmH1ebBDaVkGHoMts=
@@ -153,6 +198,8 @@ github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042
 github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
 github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
 github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
+github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b h1:FfH+VrHHk6Lxt9HdVS0PXzSXFyS2NbZKXv33FYPol0A=
+github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b/go.mod h1:AC62GU6hc0BrNm+9RK9VSiwa/EUe1bkIeFORAMcHvJU=
 github.com/pelletier/go-toml v1.0.1/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
 github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
 github.com/peterh/liner v1.0.1-0.20171122030339-3681c2a91233/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc=
@@ -185,6 +232,8 @@ github.com/rdlucklib/rdluck_tools v1.0.3 h1:iOtK2QPlPQ6CL6c1htCk5VnFCHzyG6DCfJtu
 github.com/rdlucklib/rdluck_tools v1.0.3/go.mod h1:9Onw9o4w19C8KE5lxb8GyxgRBbZweRVkQSc79v38EaA=
 github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
 github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
+github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
+github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
 github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644/go.mod h1:nkxAfR/5quYxwPZhyDxgasBMnRtBZd0FCEpawpjMUFg=
 github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18 h1:DAYUYH5869yV94zvCES9F51oYtN5oGlwjxJJz7ZCnik=
 github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18/go.mod h1:nkxAfR/5quYxwPZhyDxgasBMnRtBZd0FCEpawpjMUFg=
@@ -201,10 +250,16 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
 github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
 github.com/syndtr/goleveldb v0.0.0-20160425020131-cfa635847112/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
 github.com/syndtr/goleveldb v0.0.0-20181127023241-353a9fca669c/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
+github.com/tealeg/xlsx v1.0.5 h1:+f8oFmvY8Gw1iUXzPk+kz+4GpbDZPK1FhPiQRd+ypgE=
+github.com/tealeg/xlsx v1.0.5/go.mod h1:btRS8dz54TDnvKNosuAqxrM1QgN1udgk9O34bDCnORM=
+github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
+github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=
 github.com/ugorji/go v0.0.0-20171122102828-84cb69a8af83/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ=
 github.com/wendal/errors v0.0.0-20130201093226-f66c77a7882b/go.mod h1:Q12BUT7DqIlHRmgv3RskH+UCM/4eqVMgI0EMmlSpAXc=
 github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
@@ -220,12 +275,26 @@ github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5t
 github.com/yuin/gopher-lua v0.0.0-20171031051903-609c9cd26973/go.mod h1:aEV29XrmTYFr3CiRxZeGHpkvbwq+prZduBqMaascyCU=
 go.mongodb.org/mongo-driver v1.15.0 h1:rJCKC8eEliewXjZGf0ddURtl7tTVy1TK3bfl0gkUSLc=
 go.mongodb.org/mongo-driver v1.15.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c=
+go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
 golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
 golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
+golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
+golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
+golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
+golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
+golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
+golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
+golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
+golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -233,9 +302,12 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
 golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
 golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
+golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
+golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -244,10 +316,13 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ
 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
 golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
+golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -255,10 +330,14 @@ golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
 golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
+golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -267,11 +346,23 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
 golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
 golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
+golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
+golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
+golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
+golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
+gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0=
+gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
+gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc=
 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
 google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -293,6 +384,8 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV
 gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
 gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
 gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=
+gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
+gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
 gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
@@ -303,5 +396,8 @@ gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
 gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

+ 1 - 1
main.go

@@ -10,6 +10,6 @@ func main() {
 		beego.BConfig.WebConfig.DirectoryIndex = true
 		beego.BConfig.WebConfig.StaticDir["/swagger"] = "swagger"
 	}
-
+	//services.InitEtaForumAdminId() // 初始化账号
 	beego.Run()
 }

+ 41 - 0
models/chart_classify.go

@@ -23,6 +23,7 @@ type ChartClassify struct {
 	Source              int       `description:"1:ETA图库;2:商品价格曲线"`
 	IsJoinPermission    int       `description:"是否加入权限管控,0:不加入;1:加入;默认:0"`
 	ChartClassifyNameEn string    `description:"英文分类名称"`
+	LevelPath           string    `description:"分类的所有上级分类包括自己"`
 }
 
 func AddChartClassify(item *ChartClassify) (lastId int64, err error) {
@@ -424,3 +425,43 @@ func (m ChartClassifyItemList) Less(i, j int) bool {
 func (m ChartClassifyItemList) Swap(i, j int) {
 	m[i], m[j] = m[j], m[i]
 }
+
+func GetChartClassifyByLevelPath(levelPath string) (items []*ChartClassify, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM chart_classify where level_path like "` + levelPath + `%"`
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}
+
+type BusinessChartClassifyResp struct {
+	List      []*ChartClassifyItems
+	CheckList []int
+}
+
+func GetChartClassifyListByKeyword(keyword string) (items []*ChartClassify, err error) {
+	sql := ``
+	pars := make([]interface{}, 0)
+
+	sql = `SELECT * FROM chart_classify WHERE 1=1 `
+	if keyword != `` {
+		sql += ` AND chart_classify_name LIKE ? `
+		pars = append(pars, `%`+keyword+`%`)
+	}
+	sql += ` ORDER BY sort ASC, create_time ASC`
+
+	o := orm.NewOrm()
+	_, err = o.Raw(sql, pars...).QueryRows(&items)
+	return
+}
+
+func GetChartClassifyItemsByIdList(classifyIdList []int) (items []*ChartClassifyItems, err error) {
+	num := len(classifyIdList)
+	if num <= 0 {
+		return
+	}
+	o := orm.NewOrm()
+	sql := `SELECT * FROM chart_classify WHERE chart_classify_id in (` + utils.GetOrmInReplace(num) + `) `
+	_, err = o.Raw(sql, classifyIdList).QueryRows(&items)
+
+	return
+}

+ 169 - 0
models/chart_collect/chart.go

@@ -0,0 +1,169 @@
+package chart_collect
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"time"
+)
+
+type ChartCollect struct {
+	ChartCollectId    int       `orm:"column(chart_collect_id);pk"`
+	UserId            int       `orm:"column(user_id)" description:"用户ID"`
+	BusinessCode      string    `orm:"column(business_code)" description:"客户编码"`
+	RealName          string    `orm:"column(real_name)" description:"用户姓名"`
+	CollectTime       time.Time `orm:"column(collect_time)" description:"收藏时间"`
+	ChartInfoId       int       `orm:"column(chart_info_id)" description:"图表ID"`
+	CollectClassifyId int       `orm:"column(collect_classify_id)" description:"收藏的分类ID"`
+	ChartSource       int       `orm:"column(chart_source)" description:"图表来源"`
+	CreateTime        time.Time `orm:"column(create_time)" description:"创建时间"`
+	ModifyTime        time.Time `orm:"column(modify_time)" description:"修改时间"`
+}
+
+type ChartCollectAddReq struct {
+	ChartInfoId       int `description:"图表id"`
+	CollectClassifyId int `description:"收藏分类id"`
+}
+
+type ChartCollectView struct {
+	ChartCollectId    int       `orm:"column(chart_collect_id);pk"`
+	ChartInfoId       int       `description:"图表id"`
+	UserId            int       `description:"用户id"`
+	CreateTime        time.Time `description:"创建时间"`
+	CollectTime       string    `description:"收藏时间"`
+	CollectClassifyId int
+	ChartName         string `description:"来源名称"`
+	ChartNameEn       string `description:"英文图表名称"`
+	ChartImage        string `description:"图表图片"`
+	UniqueCode        string `description:"图表唯一编码"`
+}
+
+type ChartCollectListResp struct {
+	Paging *paging.PagingItem
+	List   []*ChartCollectView
+}
+
+type ChartCollectEditReq struct {
+	ChartCollectId    int   `description:"我的图表主键"`
+	CurrentClassifyId int   `description:"当前分类ID"`
+	ClassifyId        []int `description:"分类id,数组形式"`
+}
+
+func GetChartCollectPageByCondition(condition string, pars []interface{}, currentIndex, pageSize int) (item []*ChartCollectView, err error) {
+	o := orm.NewOrm()
+	//sql := ` SELECT * FROM chart_collect WHERE 1=1 `
+	sql := `SELECT a.*,b.chart_name_en,b.chart_name, b.unique_code, b.chart_image FROM chart_collect AS a
+                                                                    INNER JOIN chart_info AS b ON a.chart_info_id=b.chart_info_id
+			WHERE 1=1 
+			`
+	if condition != "" {
+		sql += condition
+	}
+	sql += ` GROUP BY a.chart_info_id order by a.chart_collect_id desc limit ?,?`
+	_, err = o.Raw(sql, pars, currentIndex, pageSize).QueryRows(&item)
+	return
+}
+
+func GetChartCollectDetailByCondition(condition string, pars []interface{}) (item []*ChartCollectView, err error) {
+	o := orm.NewOrm()
+	//sql := ` SELECT * FROM chart_collect WHERE 1=1 `
+	sql := `SELECT a.*,b.chart_name_en,b.chart_name, b.unique_code, b.chart_image FROM chart_collect AS a
+                                                                    INNER JOIN chart_info AS b ON a.chart_info_id=b.chart_info_id
+			WHERE 1=1 
+			`
+	if condition != "" {
+		sql += condition
+	}
+	sql += ` GROUP BY a.chart_info_id`
+	_, err = o.Raw(sql, pars).QueryRows(&item)
+	return
+}
+
+func GetChartCollectCountByCondition(condition string, pars []interface{}) (total int64, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT a.chart_info_id FROM chart_collect AS a INNER JOIN chart_info AS b ON a.chart_info_id=b.chart_info_id
+			WHERE 1=1 
+			`
+	if condition != "" {
+		sql += condition
+	}
+	sql += ` GROUP BY a.chart_info_id`
+	totalSql := `select count(1) as total from (` + sql + `) as t`
+	err = o.Raw(totalSql, pars).QueryRow(&total)
+	return
+}
+
+// ModifyChartClassifyPublicReq 修改我的图库分类是否可见
+type ModifyChartClassifyPublicReq struct {
+	ClassifyId int `description:"分类id"`
+	IsPublic   int `description:"是否所有人可见,0:仅自己可见,1:所有人可见"`
+}
+
+// CopyChartClassifyReq 复制我的图库分类
+type CopyChartClassifyReq struct {
+	ClassifyId int `description:"分类id"`
+}
+
+// ChartCollectClassifyItem 我的图表分类信息
+type ChartCollectClassifyItem struct {
+	CollectClassifyId int    `description:"分类ID"`
+	ClassifyName      string `description:"分类名称"`
+	UserId            int    `description:"创建人id"`
+	/*IsPublic        int    `description:"是否公共分类"`
+	IsCompanyPublic int    `description:"是否为用户公共分类"`*/
+	ChartNum int `description:"分类下的图表数量"`
+}
+
+// ClassifyIdAndNum 我的图表-分类ID及图表数
+type ClassifyIdAndNum struct {
+	ClassifyId int `description:"分类ID"`
+	ChartNum   int `description:"分类下的图表数量"`
+}
+
+type ChartCollectAddResp struct {
+	CollectMsg string `description:"提示信息"`
+}
+
+func GetChartCollectByUserIdChartInfoId(userId, chartInfoId int) (item *ChartCollect, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM chart_collect WHERE user_id=? AND chart_info_id=? `
+	err = o.Raw(sql, userId, chartInfoId).QueryRow(&item)
+	return
+}
+
+type ChartCollectEsItem struct {
+	UserChartInfoId           string
+	ChartInfoId               int       `description:"图表id"`
+	UserId                    int       `description:"用户id"`
+	CollectTime               time.Time `description:"收藏时间"`
+	ChartName                 string    `description:"来源名称"`
+	ChartNameEn               string    `description:"英文图表名称"`
+	ChartImage                string    `description:"图表图片"`
+	UniqueCode                string    `description:"图表唯一编码"`
+	ChartClassifyId           int       `description:"图表分类id"`
+	SysUserId                 int
+	SysUserRealName           string
+	CreateTime                time.Time
+	ModifyTime                time.Time
+	ChartSource               int
+	CollectClassifyIds        string
+	ChartCollectClassifyNames string
+}
+
+// GetChartCollectByChartInfoIdAndUserIdByCondition 获图表收藏数据
+func GetChartCollectByChartInfoIdAndUserIdByCondition(condition string, pars []interface{}) (item []*ChartCollectEsItem, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT a.user_id, CONCAT(a.user_id,"_", a.chart_info_id) as user_chart_info_id, max(a.collect_time) as collect_time,
+        	b.chart_info_id,b.chart_name,b.chart_name_en,b.chart_image,b.unique_code,b.chart_classify_id,b.sys_user_id,b.sys_user_real_name,b.create_time,b.modify_time,b.source as chart_source,
+        	GROUP_CONCAT(DISTINCT d.classify_name) AS chart_collect_classify_names,GROUP_CONCAT(DISTINCT d.collect_classify_id) AS collect_classify_ids
+			FROM  chart_collect AS a
+			INNER JOIN chart_info AS b ON a.chart_info_id=b.chart_info_id
+			INNER JOIN chart_collect_classify AS d ON a.collect_classify_id=d.collect_classify_id
+			WHERE 1=1 `
+
+	if condition != "" {
+		sql += condition
+	}
+	sql += " GROUP BY a.chart_info_id,a.user_id ORDER BY a.collect_time Desc, a.chart_collect_id DESC "
+	_, err = o.Raw(sql, pars).QueryRows(&item)
+	return
+}

+ 61 - 0
models/chart_collect/log.go

@@ -0,0 +1,61 @@
+package chart_collect
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"time"
+)
+
+type ChartCollectLog struct {
+	Id                int       `orm:"column(id);pk"`
+	UserId            int       `description:"用户ID"`
+	BusinessCode      string    `description:"客户编码"`
+	RealName          string    `description:"用户姓名"`
+	ChartInfoId       int       `description:"图表ID"`
+	CollectState      int       `description:"-1 取消收藏,1收藏"`
+	CollectTime       time.Time `description:"收藏时间"`
+	CollectClassifyId int       `description:"图表分类ID(注意:此字段的注释可能与实际意图不符,原文为'图表ID')"`
+	ChartSource       int       `description:"1:ETA图库;2:商品价格曲线;3:相关性图"`
+	CreateTime        time.Time `description:"创建时间"`
+}
+
+// 获取该用户数量
+func (u *ChartCollectLog) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrm()
+	tmpSql := `SELECT * FROM user_collect_chart_stat WHERE collect_state=1 `
+	if condition != "" {
+		tmpSql += condition
+	}
+	tmpSql += ` group by chart_info_id `
+	sql := `SELECT COUNT(1) AS count FROM (` + tmpSql + `) AS c `
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+// 获取该用户列表
+func (u *UserCollectChartStat) GetPageListByCondition(condition string, pars []interface{}, order string, startSize, pageSize int) (items []*ChartCollectLog, err error) {
+	o := orm.NewOrm()
+	tmpSql := `SELECT * FROM user_collect_chart_stat WHERE collect_state=1  `
+	if condition != "" {
+		tmpSql += condition
+	}
+	tmpSql += ` group by chart_info_id `
+	if order != "" {
+		tmpSql += ` ORDER BY ` + order + ", user_chart_stat_id DESC"
+	} else {
+		tmpSql += ` ORDER BY user_chart_stat_id DESC`
+	}
+	tmpSql += ` Limit ?,?`
+	_, err = o.Raw(tmpSql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+type UserChartDetailListResp struct {
+	Paging *paging.PagingItem
+	List   []*UserChartDetailListItem
+}
+
+type UserChartDetailListItem struct {
+	ChartInfoId int    `description:"图表ID"`
+	CollectTime string `description:"收藏时间"`
+}

+ 106 - 0
models/chart_collect/stat.go

@@ -0,0 +1,106 @@
+package chart_collect
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"time"
+)
+
+// 图表收藏每日汇总统计表
+type ChartCollectStat struct {
+	ChartCollectStatId int       `orm:"column(chart_collect_stat_id);pk"`
+	ChartInfoId        int       `description:"图表ID"`
+	ChartSource        int       `description:"1:ETA图库;2:商品价格曲线;3:相关性图"`
+	ChartName          string    `description:"图表名称"`
+	LastCollectTime    time.Time `description:"最后被收藏的时间"`
+	CollectDate        string    `description:"收藏日期"`
+	CollectNum         int       `description:"收藏的图表数量"`
+	CreateTime         time.Time `description:"创建时间"`
+	ModifyTime         time.Time `description:"修改时间"`
+}
+
+// 根据图表ID和日期获取图表收藏统计信息
+func GetChartCollectStatByChartInfoIdAndCollectDate(chartInfoId int, collectDate string) (stat *ChartCollectStat, err error) {
+	o := orm.NewOrm()
+	sql := "SELECT * FROM chart_collect_stat WHERE chart_info_id = ? AND collect_date = ?"
+	err = o.Raw(sql, chartInfoId, collectDate).QueryRow(&stat)
+	return
+}
+
+// 用户收藏图表每日汇总表
+type UserCollectChartStat struct {
+	UserChartStatId      int    `orm:"column(user_chart_stat_id);pk"`
+	UserId               int    `description:"用户ID"`
+	BusinessCode         string `description:"客户编码"`
+	EtaBusinessId        int
+	RealName             string    `description:"用户姓名"`
+	CollectDate          string    `description:"收藏日期"`
+	CollectNum           int       `description:"收藏的图表数量"`
+	LastCollectChartTime time.Time `description:"最近收藏图表时间"`
+	CreateTime           time.Time `description:"创建时间"`
+	ModifyTime           time.Time `description:"修改时间"`
+}
+
+// 获取该用户数量
+func (u *UserCollectChartStat) GetGroupUserCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrm()
+	tmpSql := `SELECT * FROM user_collect_chart_stat WHERE 1=1 `
+	if condition != "" {
+		tmpSql += condition
+	}
+	tmpSql += ` group by user_id `
+	sql := `SELECT COUNT(1) AS count FROM (` + tmpSql + `) AS c `
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+// 获取该用户列表
+func (u *UserCollectChartStat) GetGroupUserPageListByCondition(condition string, pars []interface{}, order string, startSize, pageSize int) (items []*UserCollectChartStatItem, err error) {
+	o := orm.NewOrm()
+	tmpSql := `SELECT sum(collect_num) as collect_num, max(last_collect_chart_time) as last_collect_chart_time, user_id, real_name, eta_business_id, business_code  FROM user_collect_chart_stat WHERE 1=1 `
+	if condition != "" {
+		tmpSql += condition
+	}
+	tmpSql += ` group by user_id `
+	if order != "" {
+		tmpSql += ` ORDER BY ` + order + ", user_chart_stat_id DESC"
+	} else {
+		tmpSql += ` ORDER BY collect_num desc, user_chart_stat_id DESC`
+	}
+	tmpSql += ` Limit ?,?`
+	_, err = o.Raw(tmpSql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+type UserStatListResp struct {
+	Paging *paging.PagingItem
+	List   []*UserCollectChartStatItem
+}
+
+type UserCollectChartStatItem struct {
+	UserId               int    `description:"用户ID"`
+	BusinessCode         string `description:"客户编码"`
+	RealName             string `description:"用户姓名"`
+	EtaBusinessId        int
+	BusinessName         string
+	CollectNum           int `description:"收藏的图表数量"`
+	LastCollectChartTime string
+}
+
+type ChartCollectChartStatItem struct {
+	ChartInfoId     int    `description:"图表ID"`
+	ChartName       string `description:"图表名称"`
+	LastCollectTime string `description:"最后被收藏的时间"`
+	CollectNum      int    `description:"收藏的图表数量"`
+}
+type ChartStatListResp struct {
+	List []*ChartCollectChartStatItem
+}
+
+// 获取该用户列表
+func (u *ChartCollectStat) GetChartInfoStatusNum() (items []*ChartCollectChartStatItem, err error) {
+	o := orm.NewOrm()
+	tmpSql := `SELECT sum(collect_num) as collect_num, max(last_collect_time) as last_collect_time, chart_info_id, chart_name FROM chart_collect_stat  group by chart_info_id ORDER BY sum(collect_num) DESC limit 0, 20`
+	_, err = o.Raw(tmpSql).QueryRows(&items)
+	return
+}

+ 17 - 0
models/chart_info.go

@@ -1573,6 +1573,23 @@ func GetChartInfoListByChartIdList(charIdList []string) (items []*ChartInfo, err
 	return
 }
 
+// GetChartInfoListByChartIds
+// @Description: 根据图表id列表获取列表信息
+// @param edbIdList
+// @return items
+// @return err
+func GetChartInfoListByChartIds(charIdList []int) (items []*ChartInfo, err error) {
+	num := len(charIdList)
+	if num <= 0 {
+		return
+	}
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM chart_info WHERE chart_info_id in (` + utils.GetOrmInReplace(num) + `) ORDER BY chart_info_id desc `
+
+	_, err = o.Raw(sql, charIdList).QueryRows(&items)
+	return
+}
+
 // GetChartInfoListByUserId
 // @Description: 根据图表id列表获取列表信息
 // @param userIdList []int

+ 180 - 0
models/company/company_approval_message.go

@@ -0,0 +1,180 @@
+package company
+
+import (
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+type CompanyApprovalMessage struct {
+	Id                int       `orm:"column(id);pk"`
+	CreateUserId      int       `description:"申请者id"`
+	ReceiveUserId     int       `description:"审批者id"`
+	MessageStatus     int       `description:"消息状态:0未读,1:已读"`
+	SourceType        int       `description:"消息来源类型,1:客户,2:合同,3:用印"`
+	Remark            string    `description:"备注信息"`
+	Content           string    `description:"消息内容"`
+	CompanyId         int       `description:"客户id"`
+	CompanyName       string    `description:"客户名称"`
+	CreateTime        time.Time `description:"创建时间"`
+	ModifyTime        time.Time `description:"修改时间"`
+	CompanyApprovalId int       `description:"审批单id"`
+	ApprovalStatus    int       `description:"审批状态:1:待审批,2:已审批,3:已驳回"`
+	OperationStatus   int       `description:"消息状态:1:待审批,2:已审批"`
+	MessageType       int       `description:"1:申请消息,2:审批结果,3:文字消息"`
+	MessageInfo       string    `description:"消息主要内容,json数据"`
+}
+
+// 添加审批消息
+func AddCompanyApprovalMessage(item *CompanyApprovalMessage) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Insert(item)
+	return
+}
+
+// GetCompanyApprovalMessageById 主键获取消息
+func GetCompanyApprovalMessageById(id int) (item *CompanyApprovalMessage, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM company_approval_message WHERE id = ? LIMIT 1`
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+type CompanyApprovalMessageList struct {
+	Id                int    `orm:"column(id);pk"`
+	MessageStatus     int    `description:"消息状态:0未读,1:已读,2:作废"`
+	SourceType        int    `description:"消息来源类型,1:客户,2:合同 3:用印"`
+	Remark            string `description:"备注信息"`
+	Content           string `description:"消息内容"`
+	CompanyName       string `description:"客户名称"`
+	CreateTime        string `description:"创建时间"`
+	RealName          string `description:"销售名称"`
+	CompanyApprovalId int    `description:"审批单id"`
+	ApprovalStatus    int    `description:"审批状态:1:待审批,2:已审批,3:已驳回"`
+}
+
+type CompanyApprovalMessageListResp struct {
+	List  []*CompanyApprovalMessageList
+	Total int `description:"总数据条数"`
+}
+
+// 待办消息列表
+type CompanyApprovalMessageListV2Resp struct {
+	Company      CompanyApprovalMessageListResp  `description:"客户"`
+	Contract     CompanyApprovalMessageListResp  `description:"合同"`
+	Seal         CompanyApprovalMessageListResp  `description:"用印"`
+	EdbReplace   CompanyApprovalMessageListResp  `description:"指标替换"`
+	ETATrial     ETATrialApprovalMessageListResp `description:"ETA试用"`
+	BusinessTrip CompanyApprovalMessageListResp  `description:"出差"`
+}
+
+type ETATrialApprovalMessageListResp struct {
+	List  []*ETATrialApprovalMessageList
+	Total int `description:"总数据条数"`
+}
+
+type ETATrialApprovalMessageList struct {
+	Id            int    `orm:"column(id);pk"`
+	MessageStatus int    `description:"消息状态:0未读,1:已读,2:作废"`
+	SourceType    int    `description:"消息来源类型,1:客户,2:合同 3:用印 4:指标替换 5:ETA试用"`
+	Remark        string `description:"备注信息"`
+	Content       string `description:"消息内容"`
+	CompanyName   string `description:"客户名称"`
+	CreateTime    string `description:"创建时间"`
+	RealName      string `description:"销售名称"`
+	Redirect      int    `description:"跳转路径 1用户列表 2审批列表"`
+}
+
+func GetCompanyApprovalMessageList(sysUserId int, startTime string) (items []*CompanyApprovalMessageList, err error) {
+	sql := `SELECT a.*,b.real_name FROM company_approval_message AS a
+			INNER JOIN admin AS b ON a.create_user_id=b.admin_id
+			WHERE receive_user_id=? AND a.operation_status=1 AND message_status !=2 `
+	if startTime != "" {
+		sql += ` AND create_time>='` + startTime + `'`
+	}
+	sql += ` ORDER BY create_time DESC `
+	o := orm.NewOrm()
+	_, err = o.Raw(sql, sysUserId).QueryRows(&items)
+	return
+}
+
+func GetCompanyApprovalMessageCount(sysUserId int, startTime string) (count int, err error) {
+	sql := `SELECT COUNT(1) AS count FROM company_approval_message AS a
+			INNER JOIN admin AS b ON a.create_user_id=b.admin_id
+			WHERE receive_user_id=? AND a.operation_status=1 AND message_status !=2 `
+	if startTime != "" {
+		sql += ` AND create_time>='` + startTime + `'`
+	}
+	o := orm.NewOrm()
+	err = o.Raw(sql, sysUserId).QueryRow(&count)
+	return
+}
+
+// 修改消息信息
+func ModifyCompanyApprovalMessage(companyApprovalId, sourceType int) (err error) {
+	o := orm.NewOrm()
+	sql := `UPDATE company_approval_message SET operation_status=2,modify_time=NOW() 
+WHERE company_approval_id=? AND source_type=?  AND message_type=1 AND operation_status=1 `
+	_, err = o.Raw(sql, companyApprovalId, sourceType).Exec()
+	return
+}
+
+// 申请服务更新请求参数
+type CompanyApprovalMessageReadReq struct {
+	Id int `description:"消息id"`
+}
+
+func ModifyCompanyApprovalMessageStatus(companyApprovalMessageId int) (err error) {
+	o := orm.NewOrm()
+	sql := `UPDATE company_approval_message SET message_status=1,modify_time=NOW() WHERE id=?`
+	_, err = o.Raw(sql, companyApprovalMessageId).Exec()
+	return
+}
+
+// 消息作废
+func CancelCompanyApprovalMessage(companyApprovalId, sourceType int) (err error) {
+	o := orm.NewOrm()
+	sql := `UPDATE company_approval_message SET message_status=2,modify_time=NOW() 
+WHERE company_approval_id=? AND source_type=?  AND message_type=1 AND operation_status=1 `
+	//sql := `UPDATE company_approval_message SET message_status=1,modify_time=NOW() WHERE id=?`
+	_, err = o.Raw(sql, companyApprovalId, sourceType).Exec()
+	return
+}
+func GetAdminCompanyApprovalMessageList(startTime string) (items []*CompanyApprovalMessageList, err error) {
+	sql := `SELECT a.*,b.real_name FROM company_approval_message AS a
+			INNER JOIN admin AS b ON a.create_user_id=b.admin_id
+			WHERE receive_user_id = -1 AND a.operation_status=1 AND message_status !=2 `
+	if startTime != "" {
+		sql += ` AND create_time>='` + startTime + `'`
+	}
+	sql += ` ORDER BY create_time DESC `
+	o := orm.NewOrm()
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}
+
+// 消息删除
+func DeleteCompanyApprovalMessage(companyApprovalId, sourceType int) (err error) {
+	o := orm.NewOrm()
+	sql := `DELETE FROM company_approval_message WHERE company_approval_id=? AND source_type=? `
+	_, err = o.Raw(sql, companyApprovalId, sourceType).Exec()
+	return
+}
+
+// CancelCompanyApprovalMessage 消息作废
+func IsReadCompanyApprovalMessage(companyApprovalId, sourceType int) (err error) {
+	o := orm.NewOrm()
+	sql := `UPDATE company_approval_message SET message_status=1,modify_time=NOW() 
+WHERE company_approval_id=? AND source_type=?  AND message_type=1 AND operation_status=1 `
+	_, err = o.Raw(sql, companyApprovalId, sourceType).Exec()
+	return
+}
+
+// ModifyCompanyApprovalMessageStatusByCond 条件修改已读状态
+func ModifyCompanyApprovalMessageStatusByCond(cond string, pars []interface{}) (err error) {
+	o := orm.NewOrm()
+	sql := `UPDATE company_approval_message SET message_status = 1, modify_time = NOW() WHERE 1 = 1 %s`
+	sql = fmt.Sprintf(sql, cond)
+	_, err = o.Raw(sql, pars).Exec()
+	return
+}

+ 48 - 0
models/company/company_industry.go

@@ -0,0 +1,48 @@
+package company
+
+import "github.com/beego/beego/v2/client/orm"
+
+type CompanyIndustry struct {
+	IndustryId   int    `description:"行业id"`
+	IndustryName string `description:"行业名称"`
+	ParentId     int    `description:"父级id"`
+	Classify     string `description:"分类"`
+}
+
+type CompanyIndustryItem struct {
+	IndustryId   int                       `description:"行业id"`
+	IndustryName string                    `description:"行业名称"`
+	Classify     string                    `description:"分类"`
+	Children     []*CompanyIndustryItemSub `json:"children"`
+}
+
+type CompanyIndustryItemSub struct {
+	IndustryId   int    `description:"行业id"`
+	IndustryName string `description:"行业名称"`
+	Classify     string `description:"分类"`
+}
+
+type CompanyIndustryResp struct {
+	List []*CompanyIndustryItem
+}
+
+func GetCompanyIndustry(productName string) (items []*CompanyIndustryItem, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM company_industry WHERE product_name=? AND parent_id=0 ORDER BY industry_id ASC`
+	_, err = o.Raw(sql, productName).QueryRows(&items)
+	return
+}
+
+func GetCompanyIndustryChildren(classify string, parentId int) (items []*CompanyIndustryItemSub, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM company_industry WHERE product_name=? AND parent_id=? ORDER BY industry_id ASC`
+	_, err = o.Raw(sql, classify, parentId).QueryRows(&items)
+	return
+}
+
+func GetCompanyIndustryById(industryId int) (items *CompanyIndustry, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM company_industry WHERE industry_id=? `
+	err = o.Raw(sql, industryId).QueryRow(&items)
+	return
+}

+ 25 - 0
models/company/company_seller.go

@@ -0,0 +1,25 @@
+package company
+
+type CompanySellers struct {
+	AdminId   int    `description:"系统用户id"`
+	AdminName string `description:"系统用户名称"`
+	RealName  string `description:"用户真实名称"`
+}
+
+type CompanySellerResp struct {
+	List []*CompanySellers
+}
+
+// DepartmentGroupSellersResp 销售列表(根据部门、分组来)
+type DepartmentGroupSellersResp struct {
+	List []DepartmentGroupSellers
+}
+
+type DepartmentGroupSellers struct {
+	AdminId string `description:"系统用户id"`
+	//AdminName string `description:"系统用户名称"`
+	RealName     string                   `description:"用户真实名称"`
+	ChildrenList []DepartmentGroupSellers `description:"销售列表"`
+	RoleTypeCode string                   `description:"角色编码"`
+	Authority    int                      `description:"管理权限,0:无,1:部门负责人,2:小组负责人,或者ficc销售主管,4:ficc销售组长"`
+}

+ 77 - 0
models/db.go

@@ -1,6 +1,11 @@
 package models
 
 import (
+	"eta/eta_forum_admin/models/company"
+	"eta/eta_forum_admin/models/eta_business"
+	"eta/eta_forum_admin/models/eta_training_video"
+	"eta/eta_forum_admin/models/eta_trial"
+	"eta/eta_forum_admin/models/help_doc"
 	"eta/eta_forum_admin/models/system"
 	"eta/eta_forum_admin/utils"
 	"github.com/beego/beego/v2/client/orm"
@@ -22,6 +27,16 @@ func init() {
 	initChart()
 	initEdbData()
 	initUser()
+	initETATrial()
+	initEtaBusiness()
+	initSystem()
+
+	// ETA版本更新日志
+	initEtaVersionUpdateLog()
+	// 帮助文档
+	initHelpDoc()
+	// ETA培训视频相关表
+	initEtaTrainingVideo()
 }
 
 // initChart 图表 数据表
@@ -54,3 +69,65 @@ func initUser() {
 		new(system.AdminOperateRecord),
 	)
 }
+
+// initETATrial ETA试用
+func initETATrial() {
+	orm.RegisterModel(
+		new(eta_trial.EtaTrial),
+		new(eta_trial.EtaTrialApproval),
+		//new(eta_trial.EtaTrialQuestionnaire),
+		new(company.CompanyApprovalMessage),
+	)
+}
+
+// initEtaBusiness ETA商家相关表
+func initEtaBusiness() {
+	orm.RegisterModel(
+		new(eta_business.EtaBusiness),                     // ETA商家表
+		new(eta_business.EtaBusinessContract),             // ETA合同表
+		new(eta_business.EtaBusinessOperationRecord),      // ETA操作记录表
+		new(eta_business.EtaBusinessMenu),                 // ETA商家菜单表
+		new(eta_business.EtaBusinessMenuRelate),           // ETA商家菜单关联表
+		new(eta_business.EtaBusinessConfigRelate),         // ETA商家配置关联表
+		new(eta_business.EtaBusinessMenuIcon),             // ETA商家菜单icon表
+		new(User),                                         // 商家用户表
+		new(eta_business.BusinessChartClassifyPermission), //商家图表权限表
+	)
+}
+
+// initSystem 系统表 数据表
+func initSystem() {
+	orm.RegisterModel(
+		new(system.SysRole),
+		new(system.SysMenuButton),
+		new(system.SysRoleAdmin), //管理员账号和角色映射表
+		new(system.SysRoleMenu),
+	)
+}
+
+// initEtaVersionUpdateLog ETA版本更新日志
+func initEtaVersionUpdateLog() {
+	orm.RegisterModel(
+		new(EtaVersionUpdateLog), // 更新日志表
+	)
+}
+func initHelpDoc() {
+	//注册对象
+	orm.RegisterModel(
+		new(help_doc.HelpDocClassify), //分类
+		new(help_doc.HelpDoc),         //文章
+	)
+}
+
+// initEtaTrainingVideo ETA培训视频相关表
+func initEtaTrainingVideo() {
+	orm.RegisterModel(
+		new(eta_training_video.EtaTrainingVideo),      // 视频表
+		new(eta_training_video.EtaTrainingVideoOpLog), // 视频操作记录表
+		//new(eta_training_video.EtaTrainingVideoViewLog),        // 视频访问记录表
+		new(eta_training_video.EtaTrainingVideoTag),            // 标签表
+		new(eta_training_video.EtaTrainingVideoTagRelate),      // 标签关联表
+		new(eta_training_video.EtaTrainingVideoClassify),       // 分类表
+		new(eta_training_video.EtaTrainingVideoClassifyRelate), // 分类关联表
+	)
+}

+ 57 - 0
models/eta_business/business_chart_classify_permission.go

@@ -0,0 +1,57 @@
+package eta_business
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+type BusinessChartClassifyPermission struct {
+	ChartClassifyPermissionId int       `orm:"column(chart_classify_permission_id);pk"`
+	ChartClassifyId           int       `description:"分类Id"`
+	Source                    int       `description:"分类来源: 1-图库; 2-商品价格曲线; 3-相关性图表; 6-拟合方程图表; 7-统计特征"`
+	BusinessCode              string    `description:"商户号"`
+	EtaBusinessId             int       `description:"商家ID"`
+	AdminId                   int       `description:"系统用户Id"`
+	ModifyTime                time.Time `description:"变更时间"`
+	CreateTime                time.Time `description:"关系建立时间"`
+}
+
+func AddBusinessChartClassifyPermission(list []*BusinessChartClassifyPermission, businessCode string) (err error) {
+	// 事务,先执行删除,再新增
+	o := orm.NewOrm()
+
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	sql := ` DELETE FROM business_chart_classify_permission WHERE business_code = ?`
+	_, err = to.Raw(sql, businessCode).Exec()
+	if err != nil {
+		return
+	}
+	if len(list) > 0 {
+		_, err = o.InsertMulti(len(list), list)
+		return
+	}
+	return
+}
+
+type AddBusinessChartClassifyPermissionReq struct {
+	EtaBusinessId      int    `description:"商家ID"`
+	ChartClassifyIdStr string `description:"图表分类ID,多个用英文逗号隔开"`
+}
+
+// 获取商家图表分类权限
+func GetBusinessChartClassifyPermission(businessId int) (list []*BusinessChartClassifyPermission, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM business_chart_classify_permission WHERE eta_business_id = ?`
+	_, err = o.Raw(sql, businessId).QueryRows(&list)
+	return
+}

+ 374 - 0
models/eta_business/eta_business.go

@@ -0,0 +1,374 @@
+package eta_business
+
+import (
+	"eta/eta_forum_admin/utils"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"strings"
+	"time"
+)
+
+const (
+	EtaBusinessSigningStatusFirst = iota + 1
+	EtaBusinessSigningStatusContinue
+	EtaBusinessSigningStatusTerminate
+	EtaBusinessSigningStatusWait
+)
+
+type EtaBusiness struct {
+	EtaBusinessId    int       `orm:"column(eta_business_id);pk"`
+	BusinessName     string    `description:"商家名称"`
+	BusinessCode     string    `description:"商家编码"`
+	CodeEncrypt      string    `description:"商家编码加密"`
+	CreditCode       string    `description:"社会统一信用码"`
+	RegionType       string    `description:"所属区域:国内;海外"`
+	Province         string    `description:"省份"`
+	City             string    `description:"城市"`
+	Address          string    `description:"商家地址"`
+	SellerId         int       `description:"销售ID"`
+	SellerName       string    `description:"销售名称"`
+	Leader           string    `description:"决策人"`
+	IndustryId       int       `description:"行业ID"`
+	IndustryName     string    `description:"行业名称"`
+	CapitalScale     string    `description:"资金规模"`
+	ResearchTeamSize string    `description:"研究团队规模"`
+	UserMax          int       `description:"用户上限"`
+	SigningStatus    int       `description:"签约状态:1-首次签约;2-续约中;3-已终止;4-待签约"`
+	Enable           int       `description:"状态:0-禁用;1-启用"`
+	ContractId       int       `description:"当前合约ID"`
+	SigningTime      time.Time `description:"当前合约的签约时间"`
+	ExpiredTime      time.Time `description:"当前合约的到期时间"`
+	CreateTime       time.Time `description:"创建时间"`
+	ModifyTime       time.Time `description:"更新时间"`
+	Nation           string    `description:"所属国家"`
+}
+
+func (m *EtaBusiness) TableName() string {
+	return "eta_business"
+}
+
+func (m *EtaBusiness) PrimaryId() string {
+	return EtaBusinessColumns.EtaBusinessId
+}
+
+var EtaBusinessColumns = struct {
+	EtaBusinessId    string
+	BusinessName     string
+	BusinessCode     string
+	CreditCode       string
+	RegionType       string
+	Province         string
+	City             string
+	Address          string
+	SellerId         string
+	SellerName       string
+	Leader           string
+	IndustryId       string
+	IndustryName     string
+	CapitalScale     string
+	ResearchTeamSize string
+	UserMax          string
+	SigningStatus    string
+	Enable           string
+	ContractId       string
+	SigningTime      string
+	ExpiredTime      string
+	CreateTime       string
+	ModifyTime       string
+}{
+	EtaBusinessId:    "eta_business_id",
+	BusinessName:     "business_name",
+	BusinessCode:     "business_code",
+	CreditCode:       "credit_code",
+	RegionType:       "region_type",
+	Province:         "province",
+	City:             "city",
+	Address:          "address",
+	SellerId:         "seller_id",
+	SellerName:       "seller_name",
+	Leader:           "leader",
+	IndustryId:       "industry_id",
+	IndustryName:     "industry_name",
+	CapitalScale:     "capital_scale",
+	ResearchTeamSize: "research_team_size",
+	UserMax:          "user_max",
+	SigningStatus:    "signing_status",
+	Enable:           "enable",
+	ContractId:       "contract_id",
+	SigningTime:      "signing_time",
+	ExpiredTime:      "expired_time",
+	CreateTime:       "create_time",
+	ModifyTime:       "modify_time",
+}
+
+func (m *EtaBusiness) Create() (err error) {
+	o := orm.NewOrm()
+	id, err := o.Insert(m)
+	if err != nil {
+		return
+	}
+	m.EtaBusinessId = int(id)
+	return
+}
+
+func (m *EtaBusiness) CreateMulti(items []*EtaBusiness) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	o := orm.NewOrm()
+	_, err = o.InsertMulti(len(items), items)
+	return
+}
+
+func (m *EtaBusiness) Update(cols []string) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Update(m, cols...)
+	return
+}
+
+func (m *EtaBusiness) Del() (err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	_, err = o.Raw(sql, m.EtaBusinessId).Exec()
+	return
+}
+
+func (m *EtaBusiness) GetItemById(id int) (item *EtaBusiness, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+func (m *EtaBusiness) GetItemByBusinessCode(code string) (item *EtaBusiness, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), EtaBusinessColumns.BusinessCode)
+	err = o.Raw(sql, code).QueryRow(&item)
+	return
+}
+
+func (m *EtaBusiness) GetItemByCreditCode(code string) (item *EtaBusiness, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), EtaBusinessColumns.CreditCode)
+	err = o.Raw(sql, code).QueryRow(&item)
+	return
+}
+
+func (m *EtaBusiness) GetItemByBusinessName(name string) (item *EtaBusiness, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), EtaBusinessColumns.BusinessName)
+	err = o.Raw(sql, name).QueryRow(&item)
+	return
+}
+
+func (m *EtaBusiness) GetItemByCondition(condition string, pars []interface{}) (item *EtaBusiness, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s LIMIT 1`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&item)
+	return
+}
+
+func (m *EtaBusiness) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM %s WHERE 1=1 %s`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+func (m *EtaBusiness) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*EtaBusiness, err error) {
+	o := orm.NewOrm()
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+func (m *EtaBusiness) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*EtaBusiness, err error) {
+	o := orm.NewOrm()
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s LIMIT ?,?`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+// CreateEtaBusinessAndContract 新增商家和签约
+func CreateEtaBusinessAndContract(businessItem *EtaBusiness, contractItem *EtaBusinessContract) (err error) {
+	if businessItem == nil {
+		err = fmt.Errorf("item empty")
+		return
+	}
+
+	o := orm.NewOrm()
+	tx, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = tx.Rollback()
+			return
+		}
+		_ = tx.Commit()
+	}()
+
+	// 商家
+	businessId, e := tx.Insert(businessItem)
+	if e != nil {
+		err = fmt.Errorf("business insert err: %s", e.Error())
+		return
+	}
+	businessItem.EtaBusinessId = int(businessId)
+
+	// 签约
+	if contractItem != nil {
+		contractItem.EtaBusinessId = businessItem.EtaBusinessId
+		contractId, e := tx.Insert(contractItem)
+		if e != nil {
+			err = fmt.Errorf("contract insert err: %s", e.Error())
+			return
+		}
+
+		// 更新商家当前签约ID
+		businessItem.ContractId = int(contractId)
+		cols := []string{"ContractId"}
+		_, err = tx.Update(businessItem, cols...)
+	}
+	return
+}
+
+// EtaBusinessAddReq 新增商家请求体
+type EtaBusinessAddReq struct {
+	BusinessName     string `description:"商家名称"`
+	CreditCode       string `description:"社会统一信用码"`
+	RegionType       string `description:"所属区域:国内;海外"`
+	Province         string `description:"省份"`
+	City             string `description:"城市"`
+	SellerId         int    `description:"销售ID"`
+	SellerName       string `description:"销售名称"`
+	Leader           string `description:"决策人"`
+	IndustryId       int    `description:"行业ID"`
+	IndustryName     string `description:"行业名称"`
+	CapitalScale     string `description:"资金规模"`
+	ResearchTeamSize string `description:"研究团队规模"`
+	//UserMax          int    `description:"用户上限"`
+	SigningTime string `description:"签约时间"`
+	ExpiredTime string `description:"到期时间"`
+	IsCheck     bool   `description:"是否只做校验而不实际新增(业务操作上基础信息和签约时间分成两个步骤了)"`
+	Nation      string `description:"所属国家"`
+}
+
+// EtaBusinessEditReq 编辑商家请求体
+type EtaBusinessEditReq struct {
+	EtaBusinessId    int    `description:"商家ID"`
+	Province         string `description:"省份"`
+	City             string `description:"城市"`
+	Leader           string `description:"决策人"`
+	IndustryId       int    `description:"行业ID"`
+	IndustryName     string `description:"行业名称"`
+	CapitalScale     string `description:"资金规模"`
+	ResearchTeamSize string `description:"研究团队规模"`
+	//UserMax          int    `description:"用户上限"`
+	Nation string `description:"所属国家"`
+}
+
+// EtaBusinessSigningReq 商家签约请求体
+type EtaBusinessSigningReq struct {
+	EtaBusinessId int    `description:"商家ID"`
+	SigningTime   string `description:"当前合约的签约时间"`
+	ExpiredTime   string `description:"当前合约的到期时间"`
+}
+
+// EtaBusinessEnableReq 禁启用商家请求体
+type EtaBusinessEnableReq struct {
+	EtaBusinessId int `description:"商家ID"`
+}
+
+// EtaBusinessMoveSellerReq 移动商家销售请求体
+type EtaBusinessMoveSellerReq struct {
+	EtaBusinessId int    `description:"商家ID"`
+	SellerId      int    `description:"销售ID"`
+	SellerName    string `description:"销售名称"`
+}
+
+// CreateEtaBusinessCode 生成ETA商家编码
+func CreateEtaBusinessCode() (code string, err error) {
+	var num int
+	o := orm.NewOrm()
+	sql := `SELECT COUNT(1) AS num FROM eta_business WHERE create_time >= ? `
+	err = o.Raw(sql, time.Now().Format(utils.FormatDate)).QueryRow(&num)
+	if err != nil {
+		return
+	}
+	code = "E" + time.Now().Format("20060102") + fmt.Sprintf("%02d", num)
+	return
+}
+
+// EtaBusinessListResp 商家分页列表响应体
+type EtaBusinessListResp struct {
+	List   []*EtaBusinessItem `description:"商家列表数据"`
+	Paging *paging.PagingItem `description:"分页数据"`
+}
+
+// EtaBusinessAllListResp 商家分页列表响应体
+type EtaBusinessAllListResp struct {
+	List []*EtaBusinessItem `description:"商家列表数据"`
+}
+
+// EtaBusinessItem ETA商家信息
+type EtaBusinessItem struct {
+	EtaBusinessId    int
+	BusinessName     string `description:"商家名称"`
+	BusinessCode     string `description:"商家编码"`
+	CreditCode       string `description:"社会统一信用码"`
+	RegionType       string `description:"所属区域:国内;海外"`
+	Province         string `description:"省份"`
+	City             string `description:"城市"`
+	Address          string `description:"商家地址"`
+	SellerId         int    `description:"销售ID"`
+	SellerName       string `description:"销售名称"`
+	Leader           string `description:"决策人"`
+	IndustryId       int    `description:"行业ID"`
+	IndustryName     string `description:"行业名称"`
+	CapitalScale     string `description:"资金规模"`
+	ResearchTeamSize string `description:"研究团队规模"`
+	UserMax          int    `description:"用户上限"`
+	SigningStatus    int    `description:"签约状态:1-首次签约;2-续约中;3-已终止"`
+	Enable           int    `description:"状态:0-禁用;1-启用"`
+	ContractId       int    `description:"当前合约ID"`
+	SigningTime      string `description:"当前合约的签约时间"`
+	ExpiredTime      string `description:"当前合约的到期时间"`
+	CreateTime       string `description:"创建时间"`
+	ModifyTime       string `description:"更新时间"`
+	Nation           string `description:"所属国家"`
+}
+
+// EtaBusinessEditSignReq 编辑商家签约请求体
+type EtaBusinessEditSignReq struct {
+	EtaBusinessContractId int    `description:"商家合约ID"`
+	SigningTime           string `description:"当前合约的签约时间"`
+	ExpiredTime           string `description:"当前合约的到期时间"`
+}
+
+// EtaBusinessRemoveSignReq 删除商家签约请求体
+type EtaBusinessRemoveSignReq struct {
+	EtaBusinessContractId int `description:"商家合约ID"`
+}
+
+type EtaBusinessAddResp struct {
+	EtaBusinessId int
+}

+ 118 - 0
models/eta_business/eta_business_config_relate.go

@@ -0,0 +1,118 @@
+package eta_business
+
+import (
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"strings"
+)
+
+// EtaBusinessConfigRelate ETA商家菜单api关联表
+type EtaBusinessConfigRelate struct {
+	Id            int `orm:"column(id);pk"`
+	EtaBusinessId int `description:"商家ID"`
+	ConfigCode    string `description:"配置编码"`
+	ConfigValue   string `description:"配置值"`
+}
+
+func (m *EtaBusinessConfigRelate) TableName() string {
+	return "eta_business_config_relate"
+}
+
+func (m *EtaBusinessConfigRelate) PrimaryId() string {
+	return "id"
+}
+
+
+func (m *EtaBusinessConfigRelate) CreateMulti(businessId int, items []*EtaBusinessConfigRelate) (err error) {
+	if businessId == 0 {
+		return
+	}
+	o := orm.NewOrm()
+	tx, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = tx.Rollback()
+			return
+		}
+		_ = tx.Commit()
+	}()
+
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE eta_business_id = ?`, m.TableName())
+	_, err = tx.Raw(sql, businessId).Exec()
+	if err != nil {
+		return
+	}
+
+	if len(items) > 0 {
+		_, err = tx.InsertMulti(len(items), items)
+	}
+	return
+}
+
+func (m *EtaBusinessConfigRelate) Update(cols []string) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Update(m, cols...)
+	return
+}
+
+
+func (m *EtaBusinessConfigRelate) GetItemById(id int) (item *EtaBusinessConfigRelate, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+func (m *EtaBusinessConfigRelate) GetItemByCondition(condition string, pars []interface{}) (item *EtaBusinessConfigRelate, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s LIMIT 1`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&item)
+	return
+}
+
+func (m *EtaBusinessConfigRelate) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM %s WHERE 1=1 %s`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+func (m *EtaBusinessConfigRelate) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*EtaBusinessConfigRelate, err error) {
+	o := orm.NewOrm()
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := ``
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+func (m *EtaBusinessConfigRelate) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*EtaBusinessConfigRelate, err error) {
+	o := orm.NewOrm()
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := ``
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s LIMIT ?,?`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+// EtaBusinessConfigRelateSaveReq 保存商家菜单关联
+type EtaBusinessConfigRelateSaveReq struct {
+	EtaBusinessId int   `description:"商家ID"`
+	MenuIds       []int `description:"菜单IDs"`
+	HalfMenuIds   []int `description:"半选菜单IDs-仅供前端回显用的"`
+}

+ 205 - 0
models/eta_business/eta_business_contract.go

@@ -0,0 +1,205 @@
+package eta_business
+
+import (
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"strings"
+	"time"
+)
+
+// EtaBusinessContract ETA商家合同表
+type EtaBusinessContract struct {
+	EtaBusinessContractId int       `orm:"column(eta_business_contract_id);pk"`
+	EtaBusinessId         int       `description:"ETA商家ID"`
+	SigningTime           time.Time `description:"签约时间"`
+	ExpiredTime           time.Time `description:"到期时间"`
+	IsFirst               int       `description:"是否为首份签约"`
+	CreateTime            time.Time `description:"创建时间"`
+	ModifyTime            time.Time `description:"更新时间"`
+}
+
+func (m *EtaBusinessContract) TableName() string {
+	return "eta_business_contract"
+}
+
+func (m *EtaBusinessContract) PrimaryId() string {
+	return EtaBusinessContractColumns.EtaBusinessContractId
+}
+
+var EtaBusinessContractColumns = struct {
+	EtaBusinessContractId string
+	EtaBusinessId         string
+	SigningTime           string
+	ExpiredTime           string
+	IsFirst               string
+	CreateTime            string
+	ModifyTime            string
+}{
+	EtaBusinessContractId: "eta_business_contract_id",
+	EtaBusinessId:         "eta_business_id",
+	SigningTime:           "signing_time",
+	ExpiredTime:           "expired_time",
+	IsFirst:               "is_first",
+	CreateTime:            "create_time",
+	ModifyTime:            "modify_time",
+}
+
+func (m *EtaBusinessContract) Create() (err error) {
+	o := orm.NewOrm()
+	id, err := o.Insert(m)
+	if err != nil {
+		return
+	}
+	m.EtaBusinessContractId = int(id)
+	return
+}
+
+func (m *EtaBusinessContract) CreateMulti(items []*EtaBusinessContract) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	o := orm.NewOrm()
+	_, err = o.InsertMulti(len(items), items)
+	return
+}
+
+func (m *EtaBusinessContract) Update(cols []string) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Update(m, cols...)
+	return
+}
+
+func (m *EtaBusinessContract) Del() (err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	_, err = o.Raw(sql, m.EtaBusinessContractId).Exec()
+	return
+}
+
+func (m *EtaBusinessContract) GetItemById(id int) (item *EtaBusinessContract, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+func (m *EtaBusinessContract) GetItemByCondition(condition string, pars []interface{}) (err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s LIMIT 1`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&m)
+	return
+}
+
+func (m *EtaBusinessContract) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM %s WHERE 1=1 %s`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+func (m *EtaBusinessContract) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*EtaBusinessContract, err error) {
+	o := orm.NewOrm()
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+// CreateMaybeUpdateFirst 新增签约合同, 或同时更新首次签约合同
+func (m *EtaBusinessContract) CreateMaybeUpdateFirst(item *EtaBusinessContract, changeFirst bool) (err error) {
+	// 直接新增
+	o := orm.NewOrm()
+	if !changeFirst {
+		id, e := o.Insert(item)
+		if e != nil {
+			return e
+		}
+		item.EtaBusinessContractId = int(id)
+		return
+	}
+
+	// 更新签约合同
+	tx, e := o.Begin()
+	if e != nil {
+		return e
+	}
+	defer func() {
+		if err != nil {
+			_ = tx.Rollback()
+			return
+		}
+		_ = tx.Commit()
+	}()
+
+	id, e := tx.Insert(item)
+	if e != nil {
+		err = fmt.Errorf("contract insert err: %s", e.Error())
+		return
+	}
+	item.EtaBusinessContractId = int(id)
+
+	sql := fmt.Sprintf(`UPDATE %s SET %s = 0 WHERE %s = ? AND %s <> ?`, m.TableName(), EtaBusinessContractColumns.IsFirst, EtaBusinessContractColumns.EtaBusinessId, EtaBusinessContractColumns.EtaBusinessContractId)
+	_, e = tx.Raw(sql, item.EtaBusinessId, item.EtaBusinessContractId).Exec()
+	if e != nil {
+		err = fmt.Errorf("update first contract err: %s", e.Error())
+		return
+	}
+	return
+}
+
+// UpdateMaybeUpdateFirst 编辑签约合同, 或同时更新首次签约合同
+func (m *EtaBusinessContract) UpdateMaybeUpdateFirst(item *EtaBusinessContract, changeFirst bool, updateCols []string) (err error) {
+	// 无需修改首次签约则直接更新
+	o := orm.NewOrm()
+	if !changeFirst {
+		e := item.Update(updateCols)
+		if e != nil {
+			return e
+		}
+		return
+	}
+
+	// 更新首次签约合同
+	tx, e := o.Begin()
+	if e != nil {
+		return e
+	}
+	defer func() {
+		if err != nil {
+			_ = tx.Rollback()
+			return
+		}
+		_ = tx.Commit()
+	}()
+
+	_, e = tx.Update(item, updateCols...)
+	if e != nil {
+		err = fmt.Errorf("contract update err: %s", e.Error())
+		return
+	}
+
+	sql := fmt.Sprintf(`UPDATE %s SET %s = 0 WHERE %s = ? AND %s <> ?`, m.TableName(), EtaBusinessContractColumns.IsFirst, EtaBusinessContractColumns.EtaBusinessId, EtaBusinessContractColumns.EtaBusinessContractId)
+	_, e = tx.Raw(sql, item.EtaBusinessId, item.EtaBusinessContractId).Exec()
+	if e != nil {
+		err = fmt.Errorf("update first contract err: %s", e.Error())
+		return
+	}
+	return
+}
+
+// EtaBusinessContractItem 商家签约信息
+type EtaBusinessContractItem struct {
+	EtaBusinessContractId int
+	EtaBusinessId         int    `description:"ETA商家ID"`
+	SigningTime           string `description:"签约时间"`
+	ExpiredTime           string `description:"到期时间"`
+	ExpireDay             string `description:"到期天数"`
+	Using                 bool   `description:"是否当前合约"`
+}

+ 227 - 0
models/eta_business/eta_business_menu.go

@@ -0,0 +1,227 @@
+package eta_business
+
+import (
+	"eta/eta_forum_admin/utils"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"strings"
+	"time"
+)
+
+// EtaBusinessMenu ETA商家基础菜单表
+type EtaBusinessMenu struct {
+	MenuId     int       `orm:"column(menu_id);pk"`
+	ParentId   int       `description:"父级菜单ID"`
+	Name       string    `description:"菜单名称或者按钮名称"`
+	Sort       int       `description:"排序"`
+	Path       string    `description:"路由地址"`
+	IconPath   string    `description:"菜单图标地址"`
+	Component  string    `description:"组件路径"`
+	Hidden     int       `description:"是否隐藏:1-隐藏 0-显示"`
+	IsLevel    int       `description:"是否为多级菜单:1,只有一级;2,有多级"`
+	LevelPath  string    `description:"兼容以前menu表的字段"`
+	MenuType   int       `description:"菜单类型: 0-菜单; 1-按钮; 2-字段(需要特殊处理)"`
+	ButtonCode string    `description:"按钮唯一标识"`
+	CreateTime time.Time `description:"创建时间"`
+	ModifyTime time.Time `description:"更新时间"`
+}
+
+func (m *EtaBusinessMenu) TableName() string {
+	return "eta_business_menu"
+}
+
+func (m *EtaBusinessMenu) PrimaryId() string {
+	return "menu_id"
+}
+
+func (m *EtaBusinessMenu) Create() (err error) {
+	o := orm.NewOrm()
+	id, err := o.Insert(m)
+	if err != nil {
+		return
+	}
+	m.MenuId = int(id)
+	return
+}
+
+func (m *EtaBusinessMenu) CreateMulti(items []*EtaBusinessMenu) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	o := orm.NewOrm()
+	_, err = o.InsertMulti(len(items), items)
+	return
+}
+
+func (m *EtaBusinessMenu) Update(cols []string) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Update(m, cols...)
+	return
+}
+
+func (m *EtaBusinessMenu) Del() (err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	_, err = o.Raw(sql, m.MenuId).Exec()
+	return
+}
+
+func (m *EtaBusinessMenu) MultiDel(menuIds []int) (err error) {
+	if len(menuIds) == 0 {
+		return
+	}
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s IN (%s)`, m.TableName(), m.PrimaryId(), utils.GetOrmInReplace(len(menuIds)))
+	_, err = o.Raw(sql, menuIds).Exec()
+	return
+}
+
+func (m *EtaBusinessMenu) GetItemById(id int) (item *EtaBusinessMenu, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+func (m *EtaBusinessMenu) GetItemByCondition(condition string, pars []interface{}) (item *EtaBusinessMenu, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s LIMIT 1`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&item)
+	return
+}
+
+func (m *EtaBusinessMenu) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM %s WHERE 1=1 %s`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+func (m *EtaBusinessMenu) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*EtaBusinessMenu, err error) {
+	o := orm.NewOrm()
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+func (m *EtaBusinessMenu) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*EtaBusinessMenu, err error) {
+	o := orm.NewOrm()
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s LIMIT ?,?`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+// GetMenuItemsByKeyword 关键词获取菜单
+func GetMenuItemsByKeyword(keyword string, fieldArr []string, orderRule string) (items []*EtaBusinessMenu, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT
+				%s
+			FROM
+				(
+					SELECT * FROM eta_business_menu WHERE parent_id = 0 AND name LIKE ?
+					UNION
+					SELECT * FROM eta_business_menu WHERE menu_id IN (
+						SELECT
+							parent_id
+						FROM
+							eta_business_menu
+						WHERE
+							parent_id > 0 AND name LIKE ?
+					)
+				) AS t %s`
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql = fmt.Sprintf(sql, fields, order)
+	_, err = o.Raw(sql, keyword, keyword).QueryRows(&items)
+	return
+}
+
+// EtaBusinessMenuListResp ETA商家菜单列表响应体
+type EtaBusinessMenuListResp struct {
+	ChoiceList     []int                  `description:"已选菜单"`
+	HalfChoiceList []int                  `description:"半选菜单-方便前端回显用的"`
+	List           []*EtaBusinessMenuItem `description:"菜单列表"`
+}
+
+// EtaBusinessMenuItem ETA商家菜单
+type EtaBusinessMenuItem struct {
+	MenuId       int
+	ParentId     int                    `description:"父级菜单ID"`
+	Name         string                 `description:"菜单名称或者按钮名称"`
+	RootId       int                    `description:"顶层菜单ID"`
+	Sort         int                    `description:"排序"`
+	Path         string                 `description:"路由地址"`
+	PathName     string                 `description:"路由名称"`
+	IconPath     string                 `description:"菜单图标地址"`
+	Component    string                 `description:"组件路径"`
+	Hidden       int                    `description:"是否隐藏:1-隐藏 0-显示"`
+	HiddenLayout int                    `description:"是否隐藏layout:1-隐藏 0-显示"`
+	Level        int                    `description:"菜单等级:1-2-3"`
+	MenuType     int                    `description:"菜单类型: 0-菜单; 1-按钮; 2-字段(需要特殊处理)"`
+	ButtonCode   string                 `description:"按钮/菜单唯一标识"`
+	CreateTime   string                 `description:"创建时间"`
+	Children     []*EtaBusinessMenuItem `description:"子菜单"`
+}
+
+// EtaBusinessMenuAddReq 新增菜单请求体
+type EtaBusinessMenuAddReq struct {
+	ParentId   int    `description:"父级菜单ID"`
+	Name       string `description:"菜单/按钮名称"`
+	Sort       int    `description:"排序"`
+	Path       string `description:"路由地址"`
+	IconPath   string `description:"图标地址"`
+	Component  string `description:"组件路径"`
+	Hidden     int    `description:"是否隐藏:1-隐藏 0-显示"`
+	MenuType   int    `description:"菜单类型:0-菜单; 1-按钮; 2-字段"`
+	ButtonCode string `description:"按钮唯一标识"`
+}
+
+// EtaBusinessMenuEditReq 编辑菜单请求体
+type EtaBusinessMenuEditReq struct {
+	MenuId int
+	EtaBusinessMenuAddReq
+}
+
+// EtaBusinessMenuRemoveReq 删除菜单请求体
+type EtaBusinessMenuRemoveReq struct {
+	MenuId int
+}
+
+// BusinessConf 商户配置表
+type BusinessConf struct {
+	ConfKey string `description:"配置Key"`
+	ConfVal string `description:"配置值"`
+}
+
+// SetBusinessConfReq 设置用户的配置
+type SetBusinessConfReq struct {
+	EtaBusinessId int `description:"商家ID"`
+	List          []BusinessConfReq
+}
+
+type BusinessConfReq struct {
+	ConfigCode  string `description:"配置编码"`
+	ConfigValue string `description:"配置值"`
+}

+ 117 - 0
models/eta_business/eta_business_menu_icon.go

@@ -0,0 +1,117 @@
+package eta_business
+
+import (
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"strings"
+	"time"
+)
+
+// EtaBusinessMenuIcon 菜单icon表
+type EtaBusinessMenuIcon struct {
+	IconId     int       `orm:"column(icon_id);pk"`
+	IconPath   string    `description:"icon地址"`
+	CreateTime time.Time `description:"创建时间"`
+}
+
+func (m *EtaBusinessMenuIcon) TableName() string {
+	return "eta_business_menu_icon"
+}
+
+func (m *EtaBusinessMenuIcon) PrimaryId() string {
+	return "icon_id"
+}
+
+func (m *EtaBusinessMenuIcon) Create() (err error) {
+	o := orm.NewOrm()
+	id, err := o.Insert(m)
+	if err != nil {
+		return
+	}
+	m.IconId = int(id)
+	return
+}
+
+func (m *EtaBusinessMenuIcon) CreateMulti(items []*EtaBusinessMenuIcon) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	o := orm.NewOrm()
+	_, err = o.InsertMulti(len(items), items)
+	return
+}
+
+func (m *EtaBusinessMenuIcon) Update(cols []string) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Update(m, cols...)
+	return
+}
+
+func (m *EtaBusinessMenuIcon) Del() (err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	_, err = o.Raw(sql, m.IconId).Exec()
+	return
+}
+
+func (m *EtaBusinessMenuIcon) GetItemById(id int) (item *EtaBusinessMenuIcon, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+func (m *EtaBusinessMenuIcon) GetItemByCondition(condition string, pars []interface{}) (item *EtaBusinessMenuIcon, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s LIMIT 1`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&item)
+	return
+}
+
+func (m *EtaBusinessMenuIcon) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM %s WHERE 1=1 %s`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+func (m *EtaBusinessMenuIcon) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*EtaBusinessMenuIcon, err error) {
+	o := orm.NewOrm()
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC, icon_id ASC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+func (m *EtaBusinessMenuIcon) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*EtaBusinessMenuIcon, err error) {
+	o := orm.NewOrm()
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s LIMIT ?,?`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+// EtaBusinessMenuIconItem 菜单icon
+type EtaBusinessMenuIconItem struct {
+	IconId   int
+	IconPath string `description:"icon地址"`
+}
+
+// EtaBusinessMenuIconAddReq 新增icon请求体
+type EtaBusinessMenuIconAddReq struct {
+	IconPath string `description:"icon地址"`
+}

+ 133 - 0
models/eta_business/eta_business_menu_relate.go

@@ -0,0 +1,133 @@
+package eta_business
+
+import (
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"strings"
+)
+
+// EtaBusinessMenuRelate ETA商家菜单关联表
+type EtaBusinessMenuRelate struct {
+	Id            int `orm:"column(id);pk"`
+	EtaBusinessId int `description:"商家ID"`
+	MenuId        int `description:"菜单ID"`
+	Type          int `description:"类型: 0-全选; 1-半选(此字段仅供前端回显做区分)"`
+}
+
+func (m *EtaBusinessMenuRelate) TableName() string {
+	return "eta_business_menu_relate"
+}
+
+func (m *EtaBusinessMenuRelate) PrimaryId() string {
+	return "id"
+}
+
+func (m *EtaBusinessMenuRelate) Create() (err error) {
+	o := orm.NewOrm()
+	id, err := o.Insert(m)
+	if err != nil {
+		return
+	}
+	m.MenuId = int(id)
+	return
+}
+
+func (m *EtaBusinessMenuRelate) CreateMulti(businessId int, items []*EtaBusinessMenuRelate) (err error) {
+	if businessId == 0 {
+		return
+	}
+	o := orm.NewOrm()
+	tx, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = tx.Rollback()
+			return
+		}
+		_ = tx.Commit()
+	}()
+
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE eta_business_id = ?`, m.TableName())
+	_, err = tx.Raw(sql, businessId).Exec()
+	if err != nil {
+		return
+	}
+
+	if len(items) > 0 {
+		_, err = tx.InsertMulti(len(items), items)
+	}
+	return
+}
+
+func (m *EtaBusinessMenuRelate) Update(cols []string) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Update(m, cols...)
+	return
+}
+
+func (m *EtaBusinessMenuRelate) Del() (err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	_, err = o.Raw(sql, m.MenuId).Exec()
+	return
+}
+
+func (m *EtaBusinessMenuRelate) GetItemById(id int) (item *EtaBusinessMenuRelate, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+func (m *EtaBusinessMenuRelate) GetItemByCondition(condition string, pars []interface{}) (item *EtaBusinessMenuRelate, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s LIMIT 1`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&item)
+	return
+}
+
+func (m *EtaBusinessMenuRelate) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM %s WHERE 1=1 %s`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+func (m *EtaBusinessMenuRelate) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*EtaBusinessMenuRelate, err error) {
+	o := orm.NewOrm()
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := ``
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+func (m *EtaBusinessMenuRelate) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*EtaBusinessMenuRelate, err error) {
+	o := orm.NewOrm()
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := ``
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s LIMIT ?,?`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+// EtaBusinessMenuRelateSaveReq 保存商家菜单关联
+type EtaBusinessMenuRelateSaveReq struct {
+	EtaBusinessId int   `description:"商家ID"`
+	MenuIds       []int `description:"菜单IDs"`
+	HalfMenuIds   []int `description:"半选菜单IDs-仅供前端回显用的"`
+}

+ 138 - 0
models/eta_business/eta_business_operation_record.go

@@ -0,0 +1,138 @@
+package eta_business
+
+import (
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"strings"
+	"time"
+)
+
+// 操作类型
+const (
+	EtaBusinessOperationTypeAdd = iota + 1
+	EtaBusinessOperationTypeEdit
+	EtaBusinessOperationTypeDisable
+	EtaBusinessOperationTypeEnable
+	EtaBusinessOperationTypeMoveSeller
+	EtaBusinessOperationTypeRenewalContract
+	EtaBusinessOperationTypeEditContract
+	EtaBusinessOperationTypeRemoveContract
+)
+
+// EtaBusinessOperationRecord ETA商家操作日志表
+type EtaBusinessOperationRecord struct {
+	Id              int       `orm:"column(id);pk"`
+	EtaBusinessId   int       `description:"ETA商家ID"`
+	SellerId        int       `description:"操作时所对应的销售ID"`
+	SysUserId       int       `description:"操作人ID"`
+	SysRealName     string    `description:"操作人姓名"`
+	OperationType   int       `description:"操作类型:1-新增;2-编辑;3-禁用;4-启用;5-移动销售;6-添加续约;7-编辑签约;8-删除签约"`
+	OperationRemark string    `description:"操作内容"`
+	CreateTime      time.Time `description:"创建时间"`
+}
+
+func (m *EtaBusinessOperationRecord) TableName() string {
+	return "eta_business_operation_record"
+}
+
+func (m *EtaBusinessOperationRecord) PrimaryId() string {
+	return EtaBusinessOperationRecordColumns.Id
+}
+
+var EtaBusinessOperationRecordColumns = struct {
+	Id              string
+	EtaBusinessId   string
+	SellerId        string
+	SysUserId       string
+	SysRealName     string
+	OperationType   string
+	OperationRemark string
+	CreateTime      string
+}{
+	Id:              "id",
+	EtaBusinessId:   "eta_business_id",
+	SellerId:        "seller_id",
+	SysUserId:       "sys_user_id",
+	SysRealName:     "sys_real_name",
+	OperationType:   "operation_type",
+	OperationRemark: "operation_remark",
+	CreateTime:      "create_time",
+}
+
+func (m *EtaBusinessOperationRecord) Create() (err error) {
+	o := orm.NewOrm()
+	id, err := o.Insert(m)
+	if err != nil {
+		return
+	}
+	m.Id = int(id)
+	return
+}
+
+func (m *EtaBusinessOperationRecord) CreateMulti(items []*EtaBusinessOperationRecord) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	o := orm.NewOrm()
+	_, err = o.InsertMulti(len(items), items)
+	return
+}
+
+func (m *EtaBusinessOperationRecord) Update(cols []string) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Update(m, cols...)
+	return
+}
+
+func (m *EtaBusinessOperationRecord) Del() (err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	_, err = o.Raw(sql, m.Id).Exec()
+	return
+}
+
+func (m *EtaBusinessOperationRecord) GetItemById(id int) (item *EtaBusinessOperationRecord, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+func (m *EtaBusinessOperationRecord) GetItemByCondition(condition string, pars []interface{}) (err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s LIMIT 1`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&m)
+	return
+}
+
+func (m *EtaBusinessOperationRecord) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM %s WHERE 1=1 %s`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+func (m *EtaBusinessOperationRecord) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*EtaBusinessOperationRecord, err error) {
+	o := orm.NewOrm()
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+// EtaBusinessOperationRecordItem ETA商家操作日志信息
+type EtaBusinessOperationRecordItem struct {
+	EtaBusinessId   int    `description:"ETA商家ID"`
+	SysUserId       int    `description:"操作人ID"`
+	SysRealName     string `description:"操作人姓名"`
+	OperationType   int    `description:"操作类型:1-新增;2-编辑;3-禁用;4-启用;5-移动销售;6-添加续约"`
+	OperationRemark string `description:"操作内容"`
+	CreateTime      string `description:"创建时间"`
+}

+ 303 - 0
models/eta_training_video/eta_training_video.go

@@ -0,0 +1,303 @@
+package eta_training_video
+
+import (
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"strings"
+	"time"
+)
+
+const (
+	VideoUnPublish = 0
+	VideoPublished = 1
+)
+
+// EtaTrainingVideo ETA培训视频
+type EtaTrainingVideo struct {
+	EtaTrainingVideoId int       `orm:"column(eta_training_video_id);pk"`
+	VideoCode          string    `description:"视频唯一编码"`
+	Title              string    `description:"视频标题"`
+	Introduce          string    `description:"视频简介"`
+	ClassifyIds        string    `description:"视频分类IDs, 英文逗号拼接"`
+	TagIds             string    `description:"标签IDs, 英文逗号拼接"`
+	CoverImg           string    `description:"封面图"`
+	VideoUrl           string    `description:"视频地址"`
+	PublishState       int       `description:"发布状态:0-未发布; 1-已发布"`
+	PublishTime        time.Time `description:"发布时间"`
+	ViewTotal          int       `description:"访问量"`
+	CreateTime         time.Time `description:"创建时间"`
+	ModifyTime         time.Time `description:"更新时间"`
+}
+
+func (m *EtaTrainingVideo) TableName() string {
+	return "eta_training_video"
+}
+
+func (m *EtaTrainingVideo) PrimaryId() string {
+	return VideoColumns.EtaTrainingVideoId
+}
+
+var VideoColumns = struct {
+	EtaTrainingVideoId string
+	VideoCode          string
+	Title              string
+	Introduce          string
+	ClassifyIds        string
+	TagIds             string
+	CoverImg           string
+	VideoUrl           string
+	PublishState       string
+	PublishTime        string
+	ViewTotal          string
+	CreateTime         string
+	ModifyTime         string
+}{
+	EtaTrainingVideoId: "eta_training_video_id",
+	VideoCode:          "video_code",
+	Title:              "title",
+	Introduce:          "introduce",
+	ClassifyIds:        "classify_ids",
+	TagIds:             "tag_ids",
+	CoverImg:           "cover_img",
+	VideoUrl:           "video_url",
+	PublishState:       "publish_state",
+	PublishTime:        "publish_time",
+	ViewTotal:          "view_total",
+	CreateTime:         "create_time",
+	ModifyTime:         "modify_time",
+}
+
+func (m *EtaTrainingVideo) Create() (err error) {
+	o := orm.NewOrm()
+	id, err := o.Insert(m)
+	if err != nil {
+		return
+	}
+	m.EtaTrainingVideoId = int(id)
+	return
+}
+
+func (m *EtaTrainingVideo) CreateMulti(items []*EtaTrainingVideo) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	o := orm.NewOrm()
+	_, err = o.InsertMulti(len(items), items)
+	return
+}
+
+func (m *EtaTrainingVideo) Update(cols []string) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Update(m, cols...)
+	return
+}
+
+func (m *EtaTrainingVideo) Del() (err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	_, err = o.Raw(sql, m.EtaTrainingVideoId).Exec()
+	return
+}
+
+func (m *EtaTrainingVideo) GetItemById(id int) (item *EtaTrainingVideo, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+func (m *EtaTrainingVideo) GetItemByCondition(condition string, pars []interface{}) (item *EtaTrainingVideo, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s LIMIT 1`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&item)
+	return
+}
+
+func (m *EtaTrainingVideo) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM %s WHERE 1=1 %s`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+func (m *EtaTrainingVideo) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*EtaTrainingVideo, err error) {
+	o := orm.NewOrm()
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+func (m *EtaTrainingVideo) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*EtaTrainingVideo, err error) {
+	o := orm.NewOrm()
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s LIMIT ?,?`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+func (m *EtaTrainingVideo) CreateVideoAndRelates(videoItem *EtaTrainingVideo, classifyRelates []*EtaTrainingVideoClassifyRelate, tagRelates []*EtaTrainingVideoTagRelate) (err error) {
+	o := orm.NewOrm()
+	tx, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = tx.Rollback()
+			return
+		}
+		_ = tx.Commit()
+	}()
+
+	id, e := tx.Insert(videoItem)
+	if e != nil {
+		err = fmt.Errorf("insert video err: %s", e.Error())
+		return
+	}
+	videoItem.EtaTrainingVideoId = int(id)
+
+	if len(classifyRelates) > 0 {
+		for _, cr := range classifyRelates {
+			cr.EtaTrainingVideoId = videoItem.EtaTrainingVideoId
+		}
+		if _, e = tx.InsertMulti(len(classifyRelates), classifyRelates); e != nil {
+			err = fmt.Errorf("insert multi classify relates err: %s", e.Error())
+			return
+		}
+	}
+	if len(tagRelates) > 0 {
+		for _, tr := range tagRelates {
+			tr.EtaTrainingVideoId = videoItem.EtaTrainingVideoId
+		}
+		if _, e = tx.InsertMulti(len(tagRelates), tagRelates); e != nil {
+			err = fmt.Errorf("insert multi tag relates err: %s", e.Error())
+			return
+		}
+	}
+	return
+}
+
+func (m *EtaTrainingVideo) UpdateVideoAndRelates(videoItem *EtaTrainingVideo, updateCols []string, classifyRelates []*EtaTrainingVideoClassifyRelate, tagRelates []*EtaTrainingVideoTagRelate) (err error) {
+	if videoItem.EtaTrainingVideoId <= 0 {
+		return
+	}
+	o := orm.NewOrm()
+	tx, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = tx.Rollback()
+			return
+		}
+		_ = tx.Commit()
+	}()
+
+	if len(updateCols) > 0 {
+		_, e := tx.Update(videoItem, updateCols...)
+		if e != nil {
+			err = fmt.Errorf("update video err: %s", e.Error())
+			return
+		}
+	}
+
+	// 分类
+	sql := `DELETE FROM eta_training_video_classify_relate WHERE eta_training_video_id = ?`
+	if _, e := tx.Raw(sql, videoItem.EtaTrainingVideoId).Exec(); e != nil {
+		err = fmt.Errorf("clear classify relates err: %s", e.Error())
+		return
+	}
+	if len(classifyRelates) > 0 {
+		for _, cr := range classifyRelates {
+			cr.EtaTrainingVideoId = videoItem.EtaTrainingVideoId
+		}
+		if _, e := tx.InsertMulti(len(classifyRelates), classifyRelates); e != nil {
+			err = fmt.Errorf("insert multi classify relates err: %s", e.Error())
+			return
+		}
+	}
+
+	// 标签
+	sql = `DELETE FROM eta_training_video_tag_relate WHERE eta_training_video_id = ?`
+	if _, e := tx.Raw(sql, videoItem.EtaTrainingVideoId).Exec(); e != nil {
+		err = fmt.Errorf("clear tag relates err: %s", e.Error())
+		return
+	}
+	if len(tagRelates) > 0 {
+		for _, tr := range tagRelates {
+			tr.EtaTrainingVideoId = videoItem.EtaTrainingVideoId
+		}
+		if _, e := tx.InsertMulti(len(tagRelates), tagRelates); e != nil {
+			err = fmt.Errorf("insert multi tag relates err: %s", e.Error())
+			return
+		}
+	}
+	return
+}
+
+// EtaTrainingVideoAddReq 新增视频请求体
+type EtaTrainingVideoAddReq struct {
+	Title      string `description:"视频标题"`
+	Introduce  string `description:"视频简介"`
+	CoverImg   string `description:"封面图"`
+	VideoUrl   string `description:"视频地址"`
+	ClassifyId int    `description:"分类ID"`
+	TagIds     []int  `description:"标签IDs"`
+}
+
+// EtaTrainingVideoEditReq 编辑视频请求体
+type EtaTrainingVideoEditReq struct {
+	VideoId int `description:"视频ID"`
+	EtaTrainingVideoAddReq
+}
+
+// EtaTrainingVideoPublishReq 发布/取消发布视频请求体
+type EtaTrainingVideoPublishReq struct {
+	VideoId      int `description:"视频ID"`
+	PublishState int `description:"发布状态:0-取消发布;1-发布"`
+}
+
+// EtaTrainingVideoRemoveReq 删除视频请求体
+type EtaTrainingVideoRemoveReq struct {
+	VideoId int `description:"视频ID"`
+}
+
+// EtaTrainingVideoListResp 视频分页列表响应体
+type EtaTrainingVideoListResp struct {
+	List   []*EtaTrainingVideoItem `description:"视频列表数据"`
+	Paging *paging.PagingItem      `description:"分页数据"`
+}
+
+// EtaTrainingVideoItem ETA视频信息
+type EtaTrainingVideoItem struct {
+	VideoId      int                           `description:"视频ID"`
+	VideoCode    string                        `description:"视频唯一编码"`
+	Title        string                        `description:"视频标题"`
+	Introduce    string                        `description:"视频简介"`
+	Classify     *EtaTrainingVideoClassifyItem `description:"视频分类"`
+	Tags         []*EtaTrainingVideoTagItem    `description:"视频标签"`
+	CoverImg     string                        `description:"封面图"`
+	VideoUrl     string                        `description:"视频地址"`
+	PublishState int                           `description:"发布状态:0-未发布;1-已发布"`
+	PublishTime  string                        `description:"发布时间"`
+	ViewTotal    int                           `description:"访问量"`
+	CreateTime   string                        `description:"创建时间"`
+	ModifyTime   string                        `description:"更新时间"`
+}

+ 162 - 0
models/eta_training_video/eta_training_video_classify.go

@@ -0,0 +1,162 @@
+package eta_training_video
+
+import (
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"strings"
+	"time"
+)
+
+// EtaTrainingVideoClassify 视频分类
+type EtaTrainingVideoClassify struct {
+	EtaTrainingVideoClassifyId int       `orm:"column(eta_training_video_classify_id);pk"`
+	ClassifyName               string    `description:"视频分类名称"`
+	ParentId                   int       `description:"父级ID"`
+	Sort                       int       `description:"排序"`
+	SysUserId                  int       `description:"创建人ID"`
+	SysRealName                string    `description:"创建人姓名"`
+	CreateTime                 time.Time `description:"创建时间"`
+	ModifyTime                 time.Time `description:"更新时间"`
+}
+
+func (m *EtaTrainingVideoClassify) TableName() string {
+	return "eta_training_video_classify"
+}
+
+func (m *EtaTrainingVideoClassify) PrimaryId() string {
+	return VideoClassifyColumns.EtaTrainingVideoClassifyId
+}
+
+var VideoClassifyColumns = struct {
+	EtaTrainingVideoClassifyId string
+	ClassifyName               string
+	ParentId                   string
+	Sort                       string
+	SysUserId                  string
+	SysRealName                string
+	CreateTime                 string
+	ModifyTime                 string
+}{
+	EtaTrainingVideoClassifyId: "eta_training_video_classify_id",
+	ClassifyName:               "classify_name",
+	ParentId:                   "parent_id",
+	Sort:                       "sort",
+	SysUserId:                  "sys_user_id",
+	SysRealName:                "sys_real_name",
+	CreateTime:                 "create_time",
+	ModifyTime:                 "modify_time",
+}
+
+func (m *EtaTrainingVideoClassify) Create() (err error) {
+	o := orm.NewOrm()
+	id, err := o.Insert(m)
+	if err != nil {
+		return
+	}
+	m.EtaTrainingVideoClassifyId = int(id)
+	return
+}
+
+func (m *EtaTrainingVideoClassify) CreateMulti(items []*EtaTrainingVideoClassify) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	o := orm.NewOrm()
+	_, err = o.InsertMulti(len(items), items)
+	return
+}
+
+func (m *EtaTrainingVideoClassify) Update(cols []string) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Update(m, cols...)
+	return
+}
+
+func (m *EtaTrainingVideoClassify) Del() (err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	_, err = o.Raw(sql, m.EtaTrainingVideoClassifyId).Exec()
+	return
+}
+
+func (m *EtaTrainingVideoClassify) GetItemById(id int) (item *EtaTrainingVideoClassify, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+func (m *EtaTrainingVideoClassify) GetItemByCondition(condition string, pars []interface{}) (item *EtaTrainingVideoClassify, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s LIMIT 1`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&item)
+	return
+}
+
+func (m *EtaTrainingVideoClassify) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM %s WHERE 1=1 %s`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+func (m *EtaTrainingVideoClassify) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*EtaTrainingVideoClassify, err error) {
+	o := orm.NewOrm()
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+func (m *EtaTrainingVideoClassify) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*EtaTrainingVideoClassify, err error) {
+	o := orm.NewOrm()
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s LIMIT ?,?`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+// EtaTrainingVideoClassifyAddReq 新增视频分类请求体
+type EtaTrainingVideoClassifyAddReq struct {
+	ParentId     int    `description:"父级ID"`
+	ClassifyName string `description:"视频分类名称"`
+}
+
+// EtaTrainingVideoClassifyEditReq 编辑视频分类请求体
+type EtaTrainingVideoClassifyEditReq struct {
+	ClassifyId int `description:"视频分类ID"`
+	EtaTrainingVideoClassifyAddReq
+}
+
+// EtaTrainingVideoClassifyRemoveReq 删除视频分类请求体
+type EtaTrainingVideoClassifyRemoveReq struct {
+	ClassifyId int `description:"视频分类ID"`
+}
+
+// EtaTrainingVideoClassifyResp 视频分类响应体
+type EtaTrainingVideoClassifyResp struct {
+	List []*EtaTrainingVideoClassifyItem
+}
+
+// EtaTrainingVideoClassifyItem 视频分类信息
+type EtaTrainingVideoClassifyItem struct {
+	ClassifyId   int    `description:"视频分类ID"`
+	ClassifyName string `description:"视频分类名称"`
+	ParentId     int    `description:"父级ID"`
+	Sort         int    `description:"排序"`
+	Children     []*EtaTrainingVideoClassifyItem
+}

+ 137 - 0
models/eta_training_video/eta_training_video_classify_relate.go

@@ -0,0 +1,137 @@
+package eta_training_video
+
+import (
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"strings"
+)
+
+// EtaTrainingVideoClassifyRelate 分类关联表
+type EtaTrainingVideoClassifyRelate struct {
+	Id                         int    `orm:"column(id);pk"`
+	EtaTrainingVideoId         int    `description:"视频ID"`
+	EtaTrainingVideoClassifyId int    `description:"分类ID"`
+	ClassifyParentId           int    `description:"分类父级ID"`
+	ClassifyName               string `description:"分类名称"`
+}
+
+func (m *EtaTrainingVideoClassifyRelate) TableName() string {
+	return "eta_training_video_classify_relate"
+}
+
+func (m *EtaTrainingVideoClassifyRelate) PrimaryId() string {
+	return VideoClassifyRelateColumns.Id
+}
+
+var VideoClassifyRelateColumns = struct {
+	Id                         string
+	EtaTrainingVideoId         string
+	EtaTrainingVideoClassifyId string
+	ClassifyParentId           string
+	ClassifyName               string
+}{
+	Id:                         "id",
+	EtaTrainingVideoId:         "eta_training_video_id",
+	EtaTrainingVideoClassifyId: "eta_training_video_classify_id",
+	ClassifyParentId:           "classify_parent_id",
+	ClassifyName:               "classify_name",
+}
+
+func (m *EtaTrainingVideoClassifyRelate) Create() (err error) {
+	o := orm.NewOrm()
+	id, err := o.Insert(m)
+	if err != nil {
+		return
+	}
+	m.Id = int(id)
+	return
+}
+
+func (m *EtaTrainingVideoClassifyRelate) CreateMulti(items []*EtaTrainingVideoClassifyRelate) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	o := orm.NewOrm()
+	_, err = o.InsertMulti(len(items), items)
+	return
+}
+
+func (m *EtaTrainingVideoClassifyRelate) Update(cols []string) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Update(m, cols...)
+	return
+}
+
+func (m *EtaTrainingVideoClassifyRelate) Del() (err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	_, err = o.Raw(sql, m.Id).Exec()
+	return
+}
+
+func (m *EtaTrainingVideoClassifyRelate) GetItemById(id int) (item *EtaTrainingVideoClassifyRelate, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+func (m *EtaTrainingVideoClassifyRelate) GetItemByCondition(condition string, pars []interface{}) (item *EtaTrainingVideoClassifyRelate, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s LIMIT 1`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&item)
+	return
+}
+
+func (m *EtaTrainingVideoClassifyRelate) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM %s WHERE 1=1 %s`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+func (m *EtaTrainingVideoClassifyRelate) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*EtaTrainingVideoClassifyRelate, err error) {
+	o := orm.NewOrm()
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := ``
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+func (m *EtaTrainingVideoClassifyRelate) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*EtaTrainingVideoClassifyRelate, err error) {
+	o := orm.NewOrm()
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := ``
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s LIMIT ?,?`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+// UpdateClassifyInfoByClassifyId 更新分类冗余
+func (m *EtaTrainingVideoClassifyRelate) UpdateClassifyInfoByClassifyId(classifyId, parentId int, classifyName string) (err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`UPDATE %s SET %s = ?, %s = ? WHERE %s = ?`, m.TableName(), VideoClassifyRelateColumns.ClassifyParentId, VideoClassifyRelateColumns.ClassifyName, VideoClassifyRelateColumns.EtaTrainingVideoClassifyId)
+	_, err = o.Raw(sql, parentId, classifyName, classifyId).Exec()
+	return
+}
+
+// RemoveRelateByVideoId 根据视频ID移除关联
+func (m *EtaTrainingVideoClassifyRelate) RemoveRelateByVideoId(video int) (err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ?`, m.TableName(), VideoClassifyRelateColumns.EtaTrainingVideoId)
+	_, err = o.Raw(sql, video).Exec()
+	return
+}

+ 136 - 0
models/eta_training_video/eta_training_video_op_logs.go

@@ -0,0 +1,136 @@
+package eta_training_video
+
+import (
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"strings"
+	"time"
+)
+
+const (
+	VideoOpTypeAdd = iota + 1
+	VideoOpTypeEdit
+	VideoOpTypePublish
+	VideoOpTypeCancelPublish
+	VideoOpTypeRemove
+)
+
+// EtaTrainingVideoOpLog 视频操作记录表
+type EtaTrainingVideoOpLog struct {
+	Id                 int       `orm:"column(id);pk"`
+	EtaTrainingVideoId int       `description:"视频ID"`
+	SysUserId          int       `description:"操作人ID"`
+	SysRealName        string    `description:"操作人姓名"`
+	OpType             int       `description:"操作类型:1-新增;2-编辑;3-发布;4-取消发布;5-删除"`
+	OpData             string    `description:"操作数据"`
+	CreateTime         time.Time `description:"创建时间"`
+}
+
+func (m *EtaTrainingVideoOpLog) TableName() string {
+	return "eta_training_video_op_log"
+}
+
+func (m *EtaTrainingVideoOpLog) PrimaryId() string {
+	return VideoOpLogsColumns.Id
+}
+
+var VideoOpLogsColumns = struct {
+	Id                 string
+	EtaTrainingVideoId string
+	SysUserId          string
+	SysRealName        string
+	OpType             string
+	OpData             string
+	CreateTime         string
+}{
+	Id:                 "id",
+	EtaTrainingVideoId: "eta_training_video_id",
+	SysUserId:          "sys_user_id",
+	SysRealName:        "sys_real_name",
+	OpType:             "op_type",
+	OpData:             "op_data",
+	CreateTime:         "create_time",
+}
+
+func (m *EtaTrainingVideoOpLog) Create() (err error) {
+	o := orm.NewOrm()
+	id, err := o.Insert(m)
+	if err != nil {
+		return
+	}
+	m.Id = int(id)
+	return
+}
+
+func (m *EtaTrainingVideoOpLog) CreateMulti(items []*EtaTrainingVideoOpLog) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	o := orm.NewOrm()
+	_, err = o.InsertMulti(len(items), items)
+	return
+}
+
+func (m *EtaTrainingVideoOpLog) Update(cols []string) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Update(m, cols...)
+	return
+}
+
+func (m *EtaTrainingVideoOpLog) Del() (err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	_, err = o.Raw(sql, m.Id).Exec()
+	return
+}
+
+func (m *EtaTrainingVideoOpLog) GetItemById(id int) (item *EtaTrainingVideoOpLog, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+func (m *EtaTrainingVideoOpLog) GetItemByCondition(condition string, pars []interface{}) (item *EtaTrainingVideoOpLog, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s LIMIT 1`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&item)
+	return
+}
+
+func (m *EtaTrainingVideoOpLog) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM %s WHERE 1=1 %s`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+func (m *EtaTrainingVideoOpLog) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*EtaTrainingVideoOpLog, err error) {
+	o := orm.NewOrm()
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+func (m *EtaTrainingVideoOpLog) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*EtaTrainingVideoOpLog, err error) {
+	o := orm.NewOrm()
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s LIMIT ?,?`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}

+ 179 - 0
models/eta_training_video/eta_training_video_tag.go

@@ -0,0 +1,179 @@
+package eta_training_video
+
+import (
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"strings"
+	"time"
+)
+
+// EtaTrainingVideoTag 视频标签表
+type EtaTrainingVideoTag struct {
+	EtaTrainingVideoTagId int       `orm:"column(eta_training_video_tag_id);pk"`
+	TagName               string    `description:"标签名称"`
+	SysUserId             int       `description:"创建人ID"`
+	SysRealName           string    `description:"创建人姓名"`
+	VideoTotal            int       `description:"关联视频数"`
+	CreateTime            time.Time `description:"创建时间"`
+	ModifyTime            time.Time `description:"更新时间"`
+}
+
+func (m *EtaTrainingVideoTag) TableName() string {
+	return "eta_training_video_tag"
+}
+
+func (m *EtaTrainingVideoTag) PrimaryId() string {
+	return VideoTagColumns.EtaTrainingVideoTagId
+}
+
+var VideoTagColumns = struct {
+	EtaTrainingVideoTagId string
+	TagName               string
+	SysUserId             string
+	SysRealName           string
+	VideoTotal            string
+	CreateTime            string
+	ModifyTime            string
+}{
+	EtaTrainingVideoTagId: "eta_training_video_tag_id",
+	TagName:               "tag_name",
+	SysUserId:             "sys_user_id",
+	SysRealName:           "sys_real_name",
+	VideoTotal:            "video_total",
+	CreateTime:            "create_time",
+	ModifyTime:            "modify_time",
+}
+
+func (m *EtaTrainingVideoTag) Create() (err error) {
+	o := orm.NewOrm()
+	id, err := o.Insert(m)
+	if err != nil {
+		return
+	}
+	m.EtaTrainingVideoTagId = int(id)
+	return
+}
+
+func (m *EtaTrainingVideoTag) CreateMulti(items []*EtaTrainingVideoTag) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	o := orm.NewOrm()
+	_, err = o.InsertMulti(len(items), items)
+	return
+}
+
+func (m *EtaTrainingVideoTag) Update(cols []string) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Update(m, cols...)
+	return
+}
+
+func (m *EtaTrainingVideoTag) Del() (err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	_, err = o.Raw(sql, m.EtaTrainingVideoTagId).Exec()
+	return
+}
+
+func (m *EtaTrainingVideoTag) GetItemById(id int) (item *EtaTrainingVideoTag, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+func (m *EtaTrainingVideoTag) GetItemByCondition(condition string, pars []interface{}) (item *EtaTrainingVideoTag, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s LIMIT 1`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&item)
+	return
+}
+
+func (m *EtaTrainingVideoTag) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM %s WHERE 1=1 %s`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+func (m *EtaTrainingVideoTag) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*EtaTrainingVideoTag, err error) {
+	o := orm.NewOrm()
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+func (m *EtaTrainingVideoTag) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*EtaTrainingVideoTag, err error) {
+	o := orm.NewOrm()
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s LIMIT ?,?`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+func (m *EtaTrainingVideoTag) UpdateVideoTotal(tagId int) (err error) {
+	o := orm.NewOrm()
+	sql := `UPDATE eta_training_video_tag SET video_total = (
+				SELECT COUNT(1) FROM eta_training_video_tag_relate WHERE eta_training_video_tag_id = ?
+			) WHERE eta_training_video_tag_id = ?`
+	_, err = o.Raw(sql, tagId, tagId).Exec()
+	return
+}
+
+// RemoveVideoTotalByVideoId 更新标签的视频引用数
+func (m *EtaTrainingVideoTag) RemoveVideoTotalByVideoId(videoId int) (err error) {
+	o := orm.NewOrm()
+	sql := `UPDATE eta_training_video_tag SET video_total = video_total-1 WHERE eta_training_video_tag_id IN (
+				SELECT DISTINCT eta_training_video_tag_id FROM eta_training_video_tag_relate WHERE eta_training_video_id = ?
+			) AND video_total > 0`
+	_, err = o.Raw(sql, videoId).Exec()
+	return
+}
+
+// EtaTrainingVideoTagAddReq 新增标签请求体
+type EtaTrainingVideoTagAddReq struct {
+	TagName string `description:"标签名称"`
+}
+
+// EtaTrainingVideoTagEditReq 编辑标签请求体
+type EtaTrainingVideoTagEditReq struct {
+	TagId int `description:"标签ID"`
+	EtaTrainingVideoTagAddReq
+}
+
+// EtaTrainingVideoTagRemoveReq 删除标签请求体
+type EtaTrainingVideoTagRemoveReq struct {
+	TagId int `description:"标签ID"`
+}
+
+// EtaTrainingVideoTagPageListResp 标签分页列表响应体
+type EtaTrainingVideoTagPageListResp struct {
+	List   []*EtaTrainingVideoTagItem `description:"标签列表数据"`
+	Paging *paging.PagingItem         `description:"分页数据"`
+}
+
+// EtaTrainingVideoTagItem 标签信息
+type EtaTrainingVideoTagItem struct {
+	TagId      int    `description:"标签ID"`
+	TagName    string `description:"标签名称"`
+	VideoTotal int    `description:"关联视频数"`
+	CreateTime string `description:"创建时间"`
+	ModifyTime string `description:"更新时间"`
+}

+ 142 - 0
models/eta_training_video/eta_training_video_tag_relate.go

@@ -0,0 +1,142 @@
+package eta_training_video
+
+import (
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"strings"
+)
+
+// EtaTrainingVideoTagRelate 标签关联表
+type EtaTrainingVideoTagRelate struct {
+	Id                    int    `orm:"column(id);pk"`
+	EtaTrainingVideoId    int    `description:"视频ID"`
+	EtaTrainingVideoTagId int    `description:"标签ID"`
+	TagName               string `description:"标签名称"`
+}
+
+func (m *EtaTrainingVideoTagRelate) TableName() string {
+	return "eta_training_video_tag_relate"
+}
+
+func (m *EtaTrainingVideoTagRelate) PrimaryId() string {
+	return VideoTagRelateColumns.Id
+}
+
+var VideoTagRelateColumns = struct {
+	Id                    string
+	EtaTrainingVideoId    string
+	EtaTrainingVideoTagId string
+	TagName               string
+}{
+	Id:                    "id",
+	EtaTrainingVideoId:    "eta_training_video_id",
+	EtaTrainingVideoTagId: "eta_training_video_tag_id",
+	TagName:               "tag_name",
+}
+
+func (m *EtaTrainingVideoTagRelate) Create() (err error) {
+	o := orm.NewOrm()
+	id, err := o.Insert(m)
+	if err != nil {
+		return
+	}
+	m.Id = int(id)
+	return
+}
+
+func (m *EtaTrainingVideoTagRelate) CreateMulti(items []*EtaTrainingVideoTagRelate) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	o := orm.NewOrm()
+	_, err = o.InsertMulti(len(items), items)
+	return
+}
+
+func (m *EtaTrainingVideoTagRelate) Update(cols []string) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Update(m, cols...)
+	return
+}
+
+func (m *EtaTrainingVideoTagRelate) Del() (err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	_, err = o.Raw(sql, m.Id).Exec()
+	return
+}
+
+func (m *EtaTrainingVideoTagRelate) GetItemById(id int) (item *EtaTrainingVideoTagRelate, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+func (m *EtaTrainingVideoTagRelate) GetItemByCondition(condition string, pars []interface{}) (item *EtaTrainingVideoTagRelate, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s LIMIT 1`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&item)
+	return
+}
+
+func (m *EtaTrainingVideoTagRelate) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM %s WHERE 1=1 %s`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+func (m *EtaTrainingVideoTagRelate) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*EtaTrainingVideoTagRelate, err error) {
+	o := orm.NewOrm()
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := ``
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+func (m *EtaTrainingVideoTagRelate) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*EtaTrainingVideoTagRelate, err error) {
+	o := orm.NewOrm()
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := ``
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s LIMIT ?,?`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+// UpdateTagNameByTagId 更新标签名称
+func (m *EtaTrainingVideoTagRelate) UpdateTagNameByTagId(tagId int, tagName string) (err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`UPDATE %s SET %s = ? WHERE %s = ?`, m.TableName(), VideoTagRelateColumns.TagName, VideoTagRelateColumns.EtaTrainingVideoTagId)
+	_, err = o.Raw(sql, tagName, tagId).Exec()
+	return
+}
+
+// RemoveRelateByTagId 根据标签ID移除关联
+func (m *EtaTrainingVideoTagRelate) RemoveRelateByTagId(tagId int) (err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ?`, m.TableName(), VideoTagRelateColumns.EtaTrainingVideoTagId)
+	_, err = o.Raw(sql, tagId).Exec()
+	return
+}
+
+// RemoveRelateByVideoId 根据视频ID移除关联
+func (m *EtaTrainingVideoTagRelate) RemoveRelateByVideoId(video int) (err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ?`, m.TableName(), VideoTagRelateColumns.EtaTrainingVideoId)
+	_, err = o.Raw(sql, video).Exec()
+	return
+}

+ 122 - 0
models/eta_training_video/eta_training_video_view_log.go

@@ -0,0 +1,122 @@
+package eta_training_video
+
+import (
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"strings"
+	"time"
+)
+
+// EtaTrainingVideoViewLog 视频操作记录表
+type EtaTrainingVideoViewLog struct {
+	Id                     int       `orm:"column(id);pk"`
+	EtaTrainingVideoId     int       `description:"视频ID"`
+	EtaBusinessId          int       `description:"商家ID"`
+	EtaBusinessCodeEncrypt string    `description:"商家编码encrypt"`
+	CreateTime             time.Time `description:"创建时间"`
+}
+
+func (m *EtaTrainingVideoViewLog) TableName() string {
+	return "eta_training_video_view_log"
+}
+
+func (m *EtaTrainingVideoViewLog) PrimaryId() string {
+	return VideoViewLogColumns.Id
+}
+
+var VideoViewLogColumns = struct {
+	Id                     string
+	EtaTrainingVideoId     string
+	EtaBusinessId          string
+	EtaBusinessCodeEncrypt string
+	CreateTime             string
+}{
+	Id:                     "id",
+	EtaTrainingVideoId:     "eta_training_video_id",
+	EtaBusinessId:          "eta_business_id",
+	EtaBusinessCodeEncrypt: "eta_business_code_encrypt",
+	CreateTime:             "create_time",
+}
+
+func (m *EtaTrainingVideoViewLog) Create() (err error) {
+	o := orm.NewOrm()
+	id, err := o.Insert(m)
+	if err != nil {
+		return
+	}
+	m.Id = int(id)
+	return
+}
+
+func (m *EtaTrainingVideoViewLog) CreateMulti(items []*EtaTrainingVideoViewLog) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	o := orm.NewOrm()
+	_, err = o.InsertMulti(len(items), items)
+	return
+}
+
+func (m *EtaTrainingVideoViewLog) Update(cols []string) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Update(m, cols...)
+	return
+}
+
+func (m *EtaTrainingVideoViewLog) Del() (err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	_, err = o.Raw(sql, m.Id).Exec()
+	return
+}
+
+func (m *EtaTrainingVideoViewLog) GetItemById(id int) (item *EtaTrainingVideoViewLog, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+func (m *EtaTrainingVideoViewLog) GetItemByCondition(condition string, pars []interface{}) (item *EtaTrainingVideoViewLog, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s LIMIT 1`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&item)
+	return
+}
+
+func (m *EtaTrainingVideoViewLog) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM %s WHERE 1=1 %s`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+func (m *EtaTrainingVideoViewLog) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*EtaTrainingVideoViewLog, err error) {
+	o := orm.NewOrm()
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+func (m *EtaTrainingVideoViewLog) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*EtaTrainingVideoViewLog, err error) {
+	o := orm.NewOrm()
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s LIMIT ?,?`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}

+ 177 - 0
models/eta_trial/eta_trial.go

@@ -0,0 +1,177 @@
+package eta_trial
+
+import (
+	"eta/eta_forum_admin/utils"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"time"
+)
+
+type EtaTrial struct {
+	EtaTrialId        int       `orm:"column(eta_trial_id);pk" description:"eta试用客户id"`
+	UserName          string    `description:"客户名称"`
+	CompanyName       string    `description:"客户公司姓名"`
+	Position          string    `description:"职位"`
+	Password          string    `json:"-"`
+	Account           string    `json:"-"`
+	Mobile            string    `description:"手机号"`
+	Enabled           int       `description:"1:有效,0:禁用"`
+	ActiveTime        int       `description:"累计活跃时长"`
+	LastLoginTime     time.Time `description:"最后一次登陆时间"`
+	SellerId          int       `description:"销售id"`
+	Seller            string    `description:"销售员名称"`
+	CreateTime        time.Time
+	ModifyTime        time.Time
+	LastLoginDuration int `description:"最后一次登录时长"`
+}
+
+type TrialAccountTransferReq struct {
+	EtaTrialIdList    []int  `description:"转移使用用户id列表"`
+	CurrentSellerId   int    `description:"转移后员工id"`
+	CurrentSellerName string `description:"转移后员工名称"`
+	IsCheckAll        bool   `description:"是否全选"`
+}
+
+// Update 更新用户基础信息
+func (item *EtaTrial) Update(cols []string) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Update(item, cols...)
+	return
+}
+
+func GetETATrialList(condition, sortStr string, pars []interface{}, startSize, pageSize int) (items []*ETATrialListRespItem, err error) {
+	sql := `SELECT * FROM eta_trial WHERE 1=1  `
+	if sortStr == "" {
+		sortStr = "ORDER BY create_time DESC "
+	}
+	if condition != "" {
+		sql += condition
+	}
+
+	sql += sortStr + `  LIMIT ?,? `
+	o := orm.NewOrm()
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+func GetETATrialListCount(condition string, pars []interface{}) (count int, err error) {
+	sql := `SELECT COUNT(1) AS count FROM eta_trial WHERE 1 = 1 `
+	o := orm.NewOrm()
+	if condition != "" {
+		sql += condition
+	}
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+type ETATrialListRespItem struct {
+	EtaTrialId        int    `orm:"column(eta_trial_id);pk" description:"eta试用客户id"`
+	UserName          string `description:"客户名称"`
+	CompanyName       string `description:"客户公司姓名"`
+	Position          string `description:"职位"`
+	Mobile            string `description:"手机号"`
+	ActiveTime        string `description:"累计活跃时长"`
+	LastLoginTime     string `description:"最后一次登陆时间"`
+	SellerId          int    `description:"销售id"`
+	Seller            string `description:"销售员名称"`
+	InterestModule    string `description:"感兴趣模块"`
+	Expiration        int    `description:"账号到期时长"`
+	Enabled           int    `description:"1:有效,0:禁用"`
+	IndexNum          int    `description:"累计添加指标"`
+	ChartNum          int    `description:"累计添加图表"`
+	LoginNum          int    `description:"累计登录次数"`
+	Password          string
+	Account           string
+	CreateTime        string
+	ModifyTime        string
+	LastLoginDuration string `description:"最后一次登录时长"`
+}
+
+type ETATrialListRespList struct {
+	List        []*ETATrialListRespItem
+	Paging      *paging.PagingItem
+	ApprovalNum int
+}
+
+type ETATrialAddItem struct {
+	UserName    string `description:"客户名称"`
+	CompanyName string `description:"客户公司姓名"`
+	Position    string `description:"职位"`
+	Mobile      string `description:"手机号"`
+}
+
+type ETATrialAddReq struct {
+	List []ETATrialAddItem
+}
+
+func GetETATrialByMobile(mobile string) (item *EtaTrial, err error) {
+	sql := `SELECT * FROM eta_trial WHERE mobile = ? `
+	o := orm.NewOrm()
+
+	err = o.Raw(sql, mobile).QueryRow(&item)
+	return
+}
+
+// 新增客户
+func AddETATrial(item *EtaTrial) (lastId int64, err error) {
+	o := orm.NewOrm()
+	lastId, err = o.Insert(item)
+	return
+}
+
+func GetETATrialByAccount(account string) (items []*EtaTrial, err error) {
+	sql := `SELECT * FROM eta_trial WHERE account LIKE '%` + account + `%' `
+	o := orm.NewOrm()
+
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}
+
+func UpdateETATrialEnable(mobile string) (err error) {
+	o := orm.NewOrm()
+	sql := `UPDATE eta_trial SET enabled=1,modify_time=NOW()  
+WHERE mobile=? `
+	_, err = o.Raw(sql, mobile).Exec()
+	return
+}
+
+// TransferETATrial 转移试用用户跟进销售人员
+func TransferETATrial(item *TrialAccountTransferReq) (err error) {
+	o := orm.NewOrm()
+	sql := `UPDATE eta_trial SET seller=?,seller_id=?,modify_time=NOW()  WHERE 1 = 1 `
+
+	var condition string
+	var params []interface{}
+
+	if item.IsCheckAll {
+		if len(item.EtaTrialIdList) > 0 {
+			condition = ` AND eta_trial_id not IN (` + utils.GetOrmInReplace(len(item.EtaTrialIdList)) + `)`
+			for _, id := range item.EtaTrialIdList {
+				params = append(params, id)
+			}
+		}
+	} else {
+		condition = ` AND eta_trial_id IN (` + utils.GetOrmInReplace(len(item.EtaTrialIdList)) + `)`
+		for _, id := range item.EtaTrialIdList {
+			params = append(params, id)
+		}
+	}
+	sql += condition
+	params = append([]interface{}{item.CurrentSellerName, item.CurrentSellerId}, params...)
+
+	_, err = o.Raw(sql, params...).Exec()
+
+	return
+}
+
+func GetETATrialByCondition(condition string, pars []interface{}) (item []*EtaTrial, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM eta_trial WHERE 1=1 `
+
+	sql += condition
+	_, err = o.Raw(sql, pars...).QueryRows(&item)
+	if err != nil {
+		return nil, err
+	}
+	return item, nil
+}

+ 226 - 0
models/eta_trial/eta_trial_approval.go

@@ -0,0 +1,226 @@
+package eta_trial
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"time"
+)
+
+type EtaTrialApproval struct {
+	ApprovalId      int    `orm:"column(approval_id);pk" description:"eta审批id"`
+	UserName        string `description:"客户名称"`
+	CompanyName     string `description:"客户公司姓名"`
+	Position        string `description:"职位"`
+	Mobile          string `description:"手机号"`
+	SellerId        int    `description:"销售id"`
+	Seller          string `description:"销售员名称"`
+	CreateTime      time.Time
+	ModifyTime      time.Time
+	ApprovalContent string `description:"待审内容"`
+	ApplyMethod     int    `description:"申请类型:1:申请账号 2:申请启用"`
+	ApplyReasons    string `description:"申请理由"`
+	ApprovalRemark  string `description:"审批描述"`
+	ApprovalStatus  string `description:"审批状态 '待审批','已审批','驳回','已撤回'"`
+}
+
+func GetETATrialApprovalByMobile(mobile string) (item *EtaTrialApproval, err error) {
+	sql := `SELECT * FROM eta_trial_approval WHERE mobile = ? ORDER BY create_time DESC LIMIT 1`
+	o := orm.NewOrm()
+
+	err = o.Raw(sql, mobile).QueryRow(&item)
+	return
+}
+
+func GetETATrialApprovalByCondition(condition string, pars []interface{}) (item []*EtaTrialApproval, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM eta_trial_approval WHERE 1=1 `
+
+	sql += condition
+	_, err = o.Raw(sql, pars...).QueryRows(&item)
+	if err != nil {
+		return nil, err
+	}
+	return item, nil
+}
+
+// AddETATrialApproval 新增审批
+func AddETATrialApproval(item *EtaTrialApproval) (lastId int64, err error) {
+	o := orm.NewOrm()
+	lastId, err = o.Insert(item)
+	return
+}
+
+type ETATrialListResp struct {
+	BannedList   []*ETATrialAddRespItem
+	RepeatList   []*ETATrialAddRespItem
+	SuccessList  []*ETATrialAddRespItem
+	InternalList []*ETATrialAddRespItem
+}
+
+type ETATrialAddRespItem struct {
+	UserName    string `description:"客户名称"`
+	CompanyName string `description:"客户公司姓名"`
+	Position    string `description:"职位"`
+	Mobile      string `description:"手机号"`
+	Seller      string `description:"销售"`
+}
+
+type ETAAddEnableReq struct {
+	UserName     string `description:"客户名称"`
+	CompanyName  string `description:"客户公司姓名"`
+	Position     string `description:"职位"`
+	Mobile       string `description:"手机号"`
+	ApplyReasons string `description:"申请理由"`
+}
+
+type ETAAddApplyReq struct {
+	IsCheckAll   bool     `description:"是否全选"`
+	ApplyReasons string   `description:"申请理由"`
+	MobileList   []string `description:"手机号列表"`
+}
+
+type ETATrialApplyRevokeReq struct {
+	Mobile string `description:"客户手机号"`
+}
+
+// 消息撤回
+func RevokeApproval(mobile string) (err error) {
+	o := orm.NewOrm()
+	sql := `UPDATE eta_trial_approval SET approval_status=4,modify_time=NOW() 
+WHERE mobile=? AND approval_status=1  `
+	_, err = o.Raw(sql, mobile).Exec()
+	return
+}
+
+func DelApproval(approvalId int) (err error) {
+	o := orm.NewOrm()
+	sql := `DELETE FROM eta_trial_approval WHERE approval_id = ?  `
+	_, err = o.Raw(sql, approvalId).Exec()
+	return
+}
+
+func RejectApproval(approvalId int, reason string) (err error) {
+	o := orm.NewOrm()
+	sql := `UPDATE eta_trial_approval SET approval_status=3,modify_time=NOW(), approval_remark = ? 
+WHERE approval_id=? `
+	_, err = o.Raw(sql, reason, approvalId).Exec()
+	return
+}
+
+func ApprovalTrial(approvalId int) (err error) {
+	o := orm.NewOrm()
+	sql := `UPDATE eta_trial_approval SET approval_status=2,modify_time=NOW()  
+WHERE approval_id=?  `
+	_, err = o.Raw(sql, approvalId).Exec()
+	return
+}
+
+func GetETATrialApprovalById(approvalId int) (item *EtaTrialApproval, err error) {
+	sql := `SELECT * FROM eta_trial_approval WHERE approval_id = ? `
+	o := orm.NewOrm()
+
+	err = o.Raw(sql, approvalId).QueryRow(&item)
+	return
+}
+
+func GetETATrialApprovalBySellerId(condition, sortStr string, pars []interface{}, startSize, pageSize int) (items []*EtaTrialApprovalResp, err error) {
+	sql := `SELECT
+				a.*, b.account, b.password
+			FROM
+				eta_trial_approval AS a
+			LEFT JOIN eta_trial AS b ON a.mobile = b.mobile AND a.approval_status = '已审批'
+			WHERE
+				1 = 1 AND a.seller_id = ? `
+	o := orm.NewOrm()
+	if condition != "" {
+		sql += condition
+	}
+	sql += sortStr + `  LIMIT ?,? `
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+func GetETATrialApprovalCountBySellerId(condition, sortStr string, pars []interface{}) (count int, err error) {
+	sql := `SELECT COUNT(1) AS count FROM eta_trial_approval AS a WHERE 1=1 AND a.seller_id = ? `
+	o := orm.NewOrm()
+	if condition != "" {
+		sql += condition
+	}
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+type ETATrialApprovalListRespList struct {
+	List   []*EtaTrialApprovalResp
+	Paging *paging.PagingItem
+}
+
+type EtaTrialApprovalResp struct {
+	ApprovalId      int    `orm:"column(approval_id);pk" description:"eta审批id"`
+	UserName        string `description:"客户名称"`
+	CompanyName     string `description:"客户公司姓名"`
+	Position        string `description:"职位"`
+	Mobile          string `description:"手机号"`
+	SellerId        int    `description:"销售id"`
+	Seller          string `description:"销售员名称"`
+	CreateTime      string
+	ModifyTime      string
+	ApprovalContent string `description:"待审内容"`
+	ApplyMethod     int    `description:"申请类型:1:申请账号 2:申请启用"`
+	ApplyReasons    string `description:"申请理由"`
+	ApprovalRemark  string `description:"审批描述"`
+	ApprovalStatus  string `description:"审批状态 '待审批','已审批','驳回','已撤回'"`
+	Account         string `description:"账号"`
+	Password        string `description:"密码-明文"`
+}
+
+func GetETATrialApprovalList(condition, sortStr string, pars []interface{}, startSize, pageSize int) (items []*EtaTrialApprovalResp, err error) {
+	sql := `SELECT * FROM eta_trial_approval WHERE 1=1  `
+	o := orm.NewOrm()
+	if condition != "" {
+		sql += condition
+	}
+	sql += sortStr + `  LIMIT ?,? `
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+func GetETATrialApprovalListCount(condition string, pars []interface{}) (count int, err error) {
+	sql := `SELECT COUNT(1) AS count FROM eta_trial_approval WHERE 1=1 `
+	o := orm.NewOrm()
+	if condition != "" {
+		sql += condition
+	}
+
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+type RevokeReq struct {
+	Mobile string
+}
+
+type DelReq struct {
+	ApprovalId int
+}
+
+type RejectReq struct {
+	ApprovalId   int
+	RejectReason string
+}
+
+func ResubmitTrialNoReasons(userName, companyName, position string, approvalId int) (err error) {
+	o := orm.NewOrm()
+	sql := `UPDATE eta_trial_approval SET approval_status=1,modify_time=NOW(), user_name = ?,company_name=?, position= ?  
+WHERE approval_id=? `
+	_, err = o.Raw(sql, userName, companyName, position, approvalId).Exec()
+	return
+}
+
+func ResubmitTrial(approvalId int, applyReasons string) (err error) {
+	o := orm.NewOrm()
+	sql := `UPDATE eta_trial_approval SET approval_status=1,modify_time=NOW(), apply_reasons = ?   
+WHERE approval_id=? `
+	_, err = o.Raw(sql, applyReasons, approvalId).Exec()
+	return
+}

+ 71 - 0
models/eta_trial/eta_trial_manual.go

@@ -0,0 +1,71 @@
+package eta_trial
+
+import (
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+)
+
+// ETATrialManualClassify ETA试用-手工数据分类
+type ETATrialManualClassify struct {
+	ClassifyId   int    `description:"分类ID"`
+	ClassifyName string `description:"分类名称"`
+	ParentId     int    `description:"父级ID"`
+}
+
+// GetETATrialManualClassify 获取ETA试用手工数据分类
+func GetETATrialManualClassify() (items []*ETATrialManualClassify, err error) {
+	o := orm.NewOrmUsingDB("weekly_trial")
+	sql := `SELECT classify_id, classify_name, parent_id FROM edb_trial_new.edbdata_classify WHERE is_show=1 ORDER BY sort ASC`
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}
+
+// ETATrialUserAdmin ETA试用-试用客户
+type ETATrialUserAdmin struct {
+	AdminId  int    `description:"用户ID"`
+	RealName string `description:"用户姓名"`
+}
+
+// GetNoAuthTrialUserIds 查询未分配手工权限的试用客户
+func GetNoAuthTrialUserIds() (items []*ETATrialUserAdmin, err error) {
+	o := orm.NewOrmUsingDB("weekly_trial")
+	sql := `SELECT a.admin_id, a.real_name FROM weekly_report_trial.admin AS a
+			WHERE a.admin_id NOT IN (
+				SELECT DISTINCT b.admin_id FROM hz_data_trial_new.manual_user_classify AS b
+			)
+			AND a.department_id = 12 AND a.department_name = "ETA试用客户"`
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}
+
+// InsertTrialUserManualAuth 新增试用客户手工录入权限
+func InsertTrialUserManualAuth(adminId int, adminRealName string, classifyIds []int) (err error) {
+	o := orm.NewOrmUsingDB("weekly_trial")
+	tx, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = tx.Rollback()
+		} else {
+			_ = tx.Commit()
+		}
+	}()
+
+	// 新增权限用户
+	sql := `INSERT INTO hz_data_trial_new.manual_user (admin_id, admin_real_name, sys_user_id, sys_user_real_name, create_time) VALUES ("%d", "%s", 0, "", NOW())`
+	sql = fmt.Sprintf(sql, adminId, adminRealName)
+	if _, err = tx.Raw(sql).Exec(); err != nil {
+		return
+	}
+	// 新增权限分类
+	for i := range classifyIds {
+		sql = `INSERT INTO hz_data_trial_new.manual_user_classify (admin_id, classify_id, create_time) VALUES ("%d", "%d", NOW())`
+		sql = fmt.Sprintf(sql, adminId, classifyIds[i])
+		if _, err = tx.Raw(sql).Exec(); err != nil {
+			return
+		}
+	}
+	return
+}

+ 155 - 0
models/eta_trial/eta_trial_questionnaire.go

@@ -0,0 +1,155 @@
+package eta_trial
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+type EtaTrialQuestionnaire struct {
+	QuestionnaireId int    `orm:"column(questionnaire_id);pk" description:"问卷题目id"`
+	Question        string `description:"题目"`
+	Options         string `description:"选项"`
+	Type            int    `description:"1单选 2多选 3简答题"`
+	Sort            int    `description:"排序"`
+	IsMust          int    `description:"是否必填"`
+	CreateTime      time.Time
+}
+
+// Update
+func (item *EtaTrialQuestionnaire) Update(cols []string) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Update(item, cols...)
+	return
+}
+
+// AddEtaTrialQuestionnaire
+func AddEtaTrialQuestionnaire(questionnaire *EtaTrialQuestionnaire) (err error) {
+	o := orm.NewOrm()
+	id, err := o.Insert(questionnaire)
+	if err != nil {
+		return
+	}
+	questionnaire.QuestionnaireId = int(id)
+	return
+}
+
+
+func GetETATrialQuestionnaireList(cond string) (items []*EtaTrialQuestionnaire, err error) {
+	sql := `SELECT * FROM eta_trial_questionnaire AS a WHERE 1=1  `
+	o := orm.NewOrm()
+	if cond != "" {
+		sql += cond
+	}
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}
+
+type EtaTrialQuestionnaireRespItem struct {
+	QuestionnaireId int      `orm:"column(questionnaire_id);pk" description:"问卷题目id"`
+	Question        string   `description:"题目"`
+	Type            int      `description:"1单选 2多选 3简答题"`
+	Sort            int      `description:"排序"`
+	Options          []string `description:"选项"`
+	IsMust          int      `description:"是否必填"`
+	CreateTime      string
+}
+type EtaTrialQuestionnaireResp struct {
+	List []EtaTrialQuestionnaireRespItem
+}
+
+type EtaTrialQuestionnaireSaveReq struct {
+	List []EtaTrialQuestionnaireSaveItem
+}
+
+type EtaTrialQuestionnaireSaveItem struct {
+	QuestionnaireId int      ` description:"问卷题目id"`
+	Question        string   `description:"题目"`
+	Options         []string `description:"选项"`
+	Sort            int      `description:"排序"`
+	IsMust          int      `description:"是否必填 0是 1不是"`
+	Type            int      `description:"1单选 2多选 3简答题"`
+}
+
+type EtaTrialQuestionnaireDelReq struct {
+	QuestionnaireId int `description:"问卷题目id"`
+}
+
+func GetETATrialQuestionnaireById(qId int) (count int, err error) {
+	sql := `SELECT count(1) AS count FROM eta_trial_questionnaire WHERE questionnaire_id = ?  `
+	o := orm.NewOrm()
+	err = o.Raw(sql, qId).QueryRow(&count)
+	return
+}
+
+func DelETATrialQuestionnaireById(qId int) (err error) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	//删问题
+	sql := `DELETE FROM eta_trial_questionnaire WHERE questionnaire_id = ?  `
+	_, err = to.Raw(sql, qId).Exec()
+	if err != nil {
+		return
+	}
+
+	//删选项
+	sql = `DELETE FROM eta_trial_questionnaire_options WHERE questionnaire_id = ?  `
+	_, err = to.Raw(sql, qId).Exec()
+	if err != nil {
+		return
+	}
+
+	//删记录
+	sql = `DELETE FROM eta_trial_questionnaire_record WHERE questionnaire_id = ?  `
+	_, err = to.Raw(sql, qId).Exec()
+	if err != nil {
+		return
+	}
+	return
+}
+
+func AddETATrialQuestionnaire(questions []*EtaTrialQuestionnaire) (err error) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	//新增问题
+	_, tmpErr := to.InsertMulti(len(questions), questions)
+	if tmpErr != nil {
+		err = tmpErr
+		return
+	}
+
+	return
+}
+
+func (m EtaTrialQuestionnaireResp) Len() int {
+	return len(m.List)
+}
+
+func (m EtaTrialQuestionnaireResp) Less(i, j int) bool {
+	return m.List[i].Sort < m.List[j].Sort
+}
+
+func (m EtaTrialQuestionnaireResp) Swap(i, j int) {
+	m.List[i], m.List[j] = m.List[j], m.List[i]
+}

+ 135 - 0
models/eta_trial/eta_trial_questionnaire_record.go

@@ -0,0 +1,135 @@
+package eta_trial
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"time"
+)
+
+type EtaTrialQuestionnaireRecord struct {
+	RecordId        int    `orm:"column(record_id);pk" description:"记录id"`
+	UserName        string `description:"用户名"`
+	CompanyName     string `description:"公司名"`
+	Position        string `description:"职位"`
+	Options         string `description:"选项"`
+	Mobile          string `description:"手机"`
+	Type            int    `description:"1单选 2多选 3简答题"`
+	QuestionnaireId int    `description:"题目id"`
+	CreateTime      time.Time
+}
+
+type EtaTrialQuestionnaireRecordStatistic struct {
+	QuestionnaireId int    `description:"题目id"`
+	Options         string `description:"选项"`
+	Count           int    `description:"数量"`
+	Type            int    `description:"1单选 2多选 3简答题"`
+	UserName        string `description:"用户名"`
+}
+
+func GetETATrialQuestionnaireStatistic(cond string) (items []*EtaTrialQuestionnaireRecordStatistic, err error) {
+	sql := `SELECT a.questionnaire_id, a.options,a.type,COUNT(a.options) AS count,a.user_name FROM eta_trial_questionnaire_record AS a  WHERE 1=1 `
+	o := orm.NewOrm()
+	if cond != "" {
+		sql += cond
+	}
+	sql += "GROUP BY a.options"
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}
+
+type EtaTrialQuestionnaireRecordStatisticResp struct {
+	QuestionnaireId int                                           `description:"题目id"`
+	Question        string                                        `description:"题目"`
+	Options         []*EtaTrialQuestionnaireRecordStatisticOption `description:"选项和数量"`
+	Answers         []EtaTrialQuestionnaireRecordStatisticAnswer  `description:"简答题列表"`
+	Type            int                                           `description:"1单选 2多选 3简答题"`
+	IsMust          int                                           `description:"是否必填"`
+}
+
+type EtaTrialQuestionnaireRecordStatisticAnswer struct {
+	Answer   string `description:"答案"`
+	UserName string `description:"用户名"`
+}
+
+type EtaTrialQuestionnaireRecordStatisticOption struct {
+	Index      int     `description:"索引"`
+	Option     string  `description:"选项"`
+	Count      int     `description:"数量"`
+	Percentage float64 `description:"百分比"`
+}
+
+type EtaTrialQuestionnaireRecordStatisticCount struct {
+	QuestionnaireId int `description:"题目id"`
+	Count           int `description:"数量"`
+}
+
+func GetETATrialQuestionnaireStatisticCount() (items []*EtaTrialQuestionnaireRecordStatisticCount, err error) {
+	sql := `SELECT questionnaire_id,COUNT(questionnaire_id) AS count FROM eta_trial_questionnaire_record WHERE type != 2 GROUP BY questionnaire_id `
+	o := orm.NewOrm()
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}
+
+func GetETATrialQuestionnaireAnswerListById(id, startSize, pageSize int) (items []*EtaTrialQuestionnaireRecord, err error) {
+	sql := `SELECT * FROM eta_trial_questionnaire_record WHERE questionnaire_id=? ORDER BY create_time DESC LIMIT ?,?`
+	o := orm.NewOrm()
+	_, err = o.Raw(sql, id, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+func GetETATrialQuestionnaireAnswerListByIdCount(id int) (count int, err error) {
+	sql := `SELECT count(1) FROM eta_trial_questionnaire_record WHERE questionnaire_id=? ORDER BY create_time DESC`
+	o := orm.NewOrm()
+	err = o.Raw(sql, id).QueryRow(&count)
+	return
+}
+
+func GetETATrialQuestionnaireDetailList(startSize, pageSize int) (items []*EtaTrialQuestionnaireRecord, err error) {
+	sql := `SELECT * FROM eta_trial_questionnaire_record ORDER BY create_time DESC LIMIT ?,?`
+	o := orm.NewOrm()
+	_, err = o.Raw(sql, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+func GetETATrialQuestionnaireDetailListByMobile(mobile, startDate, endDate string) (items []*EtaTrialQuestionnaireRecord, err error) {
+	sql := `SELECT * FROM eta_trial_questionnaire_record WHERE mobile=? AND create_time >= ? and create_time <= ? ORDER BY create_time DESC`
+	o := orm.NewOrm()
+	_, err = o.Raw(sql, mobile,startDate, endDate).QueryRows(&items)
+	return
+}
+
+type EtaTrialQuestionnaireDetailItem struct {
+	QuestionnaireId int      ` description:"问卷题目id"`
+	Question        string   `description:"题目"`
+	Type            int      `description:"1单选 2多选 3简答题"`
+	Sort            int      `description:"排序"`
+	Options         []string `description:"选项"`
+	UserOptions     string   `description:"回答"`
+	IsMust int
+	CreateTime      string
+}
+
+func GetETATrialQuestionnaireDetailListCount() (count int, err error) {
+	sql := `SELECT count(1) AS count FROM eta_trial_questionnaire_record  `
+	o := orm.NewOrm()
+	err = o.Raw(sql).QueryRow(&count)
+	return
+}
+
+type EtaTrialQuestionnaireRecordRespItem struct {
+	RecordId        int    `orm:"column(record_id);pk" description:"记录id"`
+	UserName        string `description:"用户名"`
+	CompanyName     string `description:"公司名"`
+	Position        string `description:"职位"`
+	Options         string `description:"选项"`
+	Mobile          string `description:"手机"`
+	Type            int    `description:"1单选 2多选 3简答题"`
+	QuestionnaireId int    `description:"题目id"`
+	IsMust          int
+	CreateTime      string
+}
+
+type QuestionnaireDetailListResp struct {
+	List   []*EtaTrialQuestionnaireRecordRespItem
+	Paging *paging.PagingItem
+}

+ 33 - 0
models/eta_trial/eta_trial_record.go

@@ -0,0 +1,33 @@
+package eta_trial
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+type EtaTrialRecord struct {
+	RecordId   int    `orm:"column(record_id);pk" description:"eta试用客户id"`
+	UserName   string `description:"客户名称"`
+	Mobile     string `description:"手机号"`
+	Part       string `description:"感兴趣板块"`
+	ActiveTime int    `description:"累计活跃时长"`
+	CreateTime time.Time
+}
+
+func GetEtaTrialRecordParts(mobiles string) (items []*EtaTrialRecord, err error) {
+	sql := `
+SELECT
+	a.mobile,
+	COUNT( part ) AS count,
+	SUBSTRING_INDEX( GROUP_CONCAT( DISTINCT part ORDER BY count DESC SEPARATOR ',' ), ',', 3 ) AS part 
+FROM
+	( SELECT *, COUNT( part ) AS count FROM eta_trial_active_record GROUP BY mobile, part ORDER BY mobile, count DESC ) AS a 
+WHERE
+	a.mobile IN (` + mobiles + `)
+GROUP BY
+	a.mobile `
+
+	o := orm.NewOrm()
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}

+ 79 - 0
models/eta_trial/sys_user.go

@@ -0,0 +1,79 @@
+package eta_trial
+
+/*
+type ETATrialAdmin struct {
+	AdminId                   int    `orm:"column(admin_id);pk" description:"系统用户id"`
+	AdminName                 string `description:"系统用户名称"`
+	AdminAvatar               string `description:"用户头像"`
+	RealName                  string `description:"系统用户姓名"`
+	Password                  string `json:"-"`
+	LastUpdatedPasswordTime   string `json:"-"`
+	Enabled                   int
+	Email                     string `description:"系统用户邮箱"`
+	LastLoginTime             string
+	CreatedTime               time.Time
+	LastUpdatedTime           string
+	Role                      string    `description:"系统用户角色"`
+	Mobile                    string    `description:"手机号"`
+	RoleType                  int       `description:"角色类型:1需要录入指标,0:不需要"`
+	RoleId                    int       `description:"角色ID"`
+	RoleName                  string    `description:"角色名称"`
+	RoleTypeCode              string    `description:"角色类型编码"`
+	DepartmentId              int       `description:"部门id"`
+	DepartmentName            string    `description:"部门名称"`
+	GroupId                   int       `description:"分组id"`
+	GroupName                 string    `description:"分组名称"`
+	Authority                 int       `description:"管理权限,0:无,1:部门负责人,2:小组负责人,或者ficc销售主管,3:超级管理员,4:ficc销售组长"`
+	Position                  string    `description:"职位"`
+	DisableTime               time.Time `description:"禁用时间"`
+	ChartPermission           int8      `description:"图表权限id"`
+	OpenId                    string    `description:"弘则部门公众号的openid"`
+	UnionId                   string    `description:"微信公众平台唯一标识"`
+	EdbPermission             int8      `description:"指标库操作权限,0:只能操作 自己的,1:所有指标可操作"`
+	MysteelChemicalPermission int8      `description:"钢联化工指标操作权限,0:只能操作 自己的,1:所有指标可操作"`
+}
+
+func (item *ETATrialAdmin) Update(cols []string) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Update(item, cols...)
+	return
+}
+
+func AddAdmin(item *ETATrialAdmin) (lastId int, err error) {
+	o := orm.NewOrmUsingDB("weekly_trial")
+	sql := `INSERT INTO admin ( admin_name, real_name, password, last_updated_password_time, enabled, last_login_time, created_time, last_updated_time, mobile, role_id, role_name, role_type_code, department_id, department_name, role, position) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )`
+	_, err = o.Raw(sql, item.AdminName, item.RealName, item.Password, item.LastUpdatedPasswordTime, item.Enabled, item.LastLoginTime, item.CreatedTime, item.LastUpdatedTime, item.Mobile, item.RoleId, item.RoleName, item.RoleTypeCode, item.DepartmentId, item.DepartmentName, item.Role, item.Position).Exec()
+
+	sql = `SELECT MAX(admin_id) FROM admin`
+	err = o.Raw(sql).QueryRow(&lastId)
+	return
+}
+
+func GetSysUserByRoleAndDepartment(role, dep string) (item *ETATrialAdmin, err error) {
+	sql := `SELECT * FROM admin WHERE role_name=? AND department_name=? LIMIT 1`
+	o := orm.NewOrmUsingDB("weekly_trial")
+	err = o.Raw(sql, role, dep).QueryRow(&item)
+	return
+}
+
+// GetSysAdminCountByMobile 查询系统中是否存在该手机号(如果有传入用户id,那么排除该用户)
+func GetSysAdminCountByMobile(mobile string, adminId int) (count int, err error) {
+	sql := `SELECT COUNT(1) AS count FROM admin WHERE mobile=? `
+	if adminId > 0 {
+		sql += ` AND admin_id != ` + fmt.Sprint(adminId)
+	}
+	o := orm.NewOrmUsingDB("weekly_trial")
+	err = o.Raw(sql, mobile).QueryRow(&count)
+	return
+}
+
+func (item *ETATrialAdmin) TableName() string {
+	return "admin"
+}
+
+func UpdateAdminEnable(mobile string) (err error) {
+	sql := `UPDATE admin SET enabled=1 WHERE mobile=? `
+	o := orm.NewOrmUsingDB("weekly_trial")
+	_, err = o.Raw(sql, mobile).Exec()
+	return
+}*/

+ 144 - 0
models/eta_version_update_log.go

@@ -0,0 +1,144 @@
+package models
+
+import (
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"strings"
+	"time"
+)
+
+// EtaVersionUpdateLog ETA版本更新日志表
+type EtaVersionUpdateLog struct {
+	Id         int       `orm:"column(id);pk"`
+	Version    string    `description:"版本号"`
+	Content    string    `description:"更新内容"`
+	UpdateDate time.Time `description:"更新日期"`
+	CreateTime time.Time
+	ModifyTime time.Time
+}
+
+func (m *EtaVersionUpdateLog) TableName() string {
+	return "eta_version_update_log"
+}
+
+func (m *EtaVersionUpdateLog) PrimaryId() string {
+	return "id"
+}
+
+func (m *EtaVersionUpdateLog) Create() (err error) {
+	o := orm.NewOrm()
+	id, err := o.Insert(m)
+	if err != nil {
+		return
+	}
+	m.Id = int(id)
+	return
+}
+
+func (m *EtaVersionUpdateLog) CreateMulti(items []*EtaVersionUpdateLog) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	o := orm.NewOrm()
+	_, err = o.InsertMulti(len(items), items)
+	return
+}
+
+func (m *EtaVersionUpdateLog) Update(cols []string) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Update(m, cols...)
+	return
+}
+
+func (m *EtaVersionUpdateLog) Del() (err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	_, err = o.Raw(sql, m.Id).Exec()
+	return
+}
+
+func (m *EtaVersionUpdateLog) GetItemById(id int) (item *EtaVersionUpdateLog, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+func (m *EtaVersionUpdateLog) GetItemByCondition(condition string, pars []interface{}) (item *EtaVersionUpdateLog, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s LIMIT 1`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&item)
+	return
+}
+
+func (m *EtaVersionUpdateLog) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM %s WHERE 1=1 %s`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+func (m *EtaVersionUpdateLog) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*EtaVersionUpdateLog, err error) {
+	o := orm.NewOrm()
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+func (m *EtaVersionUpdateLog) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*EtaVersionUpdateLog, err error) {
+	o := orm.NewOrm()
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s LIMIT ?,?`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+// EtaVersionUpdateLogItem 更新日志信息
+type EtaVersionUpdateLogItem struct {
+	Id         int
+	Version    string `description:"版本号"`
+	Content    string `description:"更新内容"`
+	UpdateDate string `description:"更新日期"`
+	CreateTime string
+	ModifyTime string
+}
+
+// EtaVersionUpdateLogListResp 更新日志列表响应体
+type EtaVersionUpdateLogListResp struct {
+	List   []*EtaVersionUpdateLogItem
+	Paging *paging.PagingItem
+}
+
+// EtaVersionUpdateLogAddReq 新增更新日志请求体
+type EtaVersionUpdateLogAddReq struct {
+	Version    string `description:"版本号"`
+	Content    string `description:"更新内容"`
+	UpdateDate string `description:"更新日期"`
+}
+
+// EtaVersionUpdateLogEditReq 编辑更新日志请求体
+type EtaVersionUpdateLogEditReq struct {
+	Id int
+	EtaVersionUpdateLogAddReq
+}
+
+// EtaVersionUpdateLogDelReq 删除更新日志请求体
+type EtaVersionUpdateLogDelReq struct {
+	Id int
+}

+ 260 - 0
models/help_doc/classify.go

@@ -0,0 +1,260 @@
+package help_doc
+
+import (
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+type HelpDocClassify struct {
+	ClassifyId      int       `orm:"column(classify_id);pk"`
+	ClassifyName    string    `description:"分类名称"`
+	ParentId        int       `description:"父级id"`
+	CreateTime      time.Time `description:"创建时间"`
+	ModifyTime      time.Time `description:"修改时间"`
+	SysUserId       int       `description:"创建人id"`
+	SysUserRealName string    `description:"创建人姓名"`
+	Level           int       `description:"层级"`
+	Sort            int       `description:"排序字段,越小越靠前,默认值:10"`
+}
+
+func AddHelpDocClassify(item *HelpDocClassify) (lastId int64, err error) {
+	o := orm.NewOrm()
+	lastId, err = o.Insert(item)
+	return
+}
+
+type AddHelpDocClassifyReq struct {
+	HelpDocClassifyName string `description:"分类名称"`
+	ParentId            int    `description:"父级id,第一级传0"`
+	Level               int    `description:"层级,第一级传0,其余传上一级的层级"`
+}
+
+func GetHelpDocClassifyCount(HelpDocClassifyName string, parentId int) (count int, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT COUNT(1) AS count FROM help_doc_classify WHERE parent_id=? AND classify_name=? `
+	err = o.Raw(sql, parentId, HelpDocClassifyName).QueryRow(&count)
+	return
+}
+
+type EditHelpDocClassifyReq struct {
+	HelpDocClassifyName string `description:"分类名称"`
+	HelpDocClassifyId   int    `description:"分类id"`
+}
+
+func GetHelpDocClassifyById(classifyId int) (item *HelpDocClassify, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM help_doc_classify WHERE classify_id=? `
+	err = o.Raw(sql, classifyId).QueryRow(&item)
+	return
+}
+
+func GetHelpDocClassifyCountById(classifyId int) (count int, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT count(1) AS count FROM help_doc_classify WHERE classify_id=? `
+	err = o.Raw(sql, classifyId).QueryRow(&count)
+	return
+}
+
+func EditHelpDocClassify(classifyId int, HelpDocClassifyName string) (err error) {
+	o := orm.NewOrm()
+	sql := `UPDATE help_doc_classify SET classify_name=?, modify_time=NOW() WHERE classify_id=? `
+	_, err = o.Raw(sql, HelpDocClassifyName, classifyId).Exec()
+	return
+}
+
+// GetHelpDocClassifyByParentId
+func GetHelpDocClassifyByParentId(parentId int, keyWord string) (items []HelpDocClassifyItems, err error) {
+	o := orm.NewOrm()
+	sql := ``
+	if keyWord != "" {
+		sql = ` SELECT * FROM help_doc_classify WHERE parent_id=? AND (classify_name LIKE '%` + keyWord + `%' ) order by sort asc,classify_id asc`
+	} else {
+		sql = ` SELECT * FROM help_doc_classify WHERE parent_id=? order by sort asc,classify_id asc`
+	}
+	_, err = o.Raw(sql, parentId).QueryRows(&items)
+	return
+}
+
+// GetHelpDocClassifyAll
+func GetHelpDocClassifyAll(keyWord string) (items []*HelpDocClassifyItems, err error) {
+	o := orm.NewOrm()
+	sql := ``
+	if keyWord != "" {
+		sql = ` SELECT * FROM help_doc_classify WHERE parent_id<>0 AND (classify_name LIKE '%` + keyWord + `%' ) order by sort asc,classify_id asc`
+	} else {
+		sql = ` SELECT * FROM help_doc_classify WHERE parent_id<>0 order by sort asc,classify_id asc`
+	}
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}
+
+type HelpDocClassifyItems struct {
+	ClassifyId         int `description:"分类id"`
+	ClassifyName       string
+	ParentId           int
+	Level              int    `description:"层级"`
+	Sort               int    `description:"排序字段,越小越靠前,默认值:10"`
+	SysUserId          int    `description:"创建人id"`
+	SysUserRealName    string `description:"创建人姓名"`
+	VisibleBusinessIds string
+	Disabled           bool `description:"是否能选 true不能 false可以"`
+	Children           []*HelpDocClassifyItems
+}
+
+type HelpDocClassifyListResp struct {
+	AllNodes      []*HelpDocClassifyItems
+	TwoLevelNodes []*HelpDocClassifyItems
+}
+
+type HelpDocClassifyDeleteCheckResp struct {
+	DeleteStatus int    `description:"检测状态:0:默认值,如果为0,继续走其他校验,1:该分类下关联不可删除,2:确认删除当前目录及包含的子目录吗"`
+	TipsMsg      string `description:"提示信息"`
+}
+
+type HelpDocClassifyDeleteCheckReq struct {
+	HelpDocClassifyId int `description:"分类id"`
+	ChartInfoId       int `description:"指标id"`
+}
+
+func GetHelpDocClassifyByCondition(condition string, pars []interface{}) (item *HelpDocClassify, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM help_doc_classify WHERE 1=1 `
+	if condition != "" {
+		sql += condition
+	}
+	err = o.Raw(sql, pars).QueryRow(&item)
+	return
+}
+
+// MoveHelpDocClassifyReq 移动分类请求参数
+type MoveHelpDocClassifyReq struct {
+	ClassifyId       int `description:"分类id"`
+	ParentClassifyId int `description:"父级分类id"`
+	PrevClassifyId   int `description:"上一个兄弟节点分类id"`
+	NextClassifyId   int `description:"下一个兄弟节点分类id"`
+}
+
+// GetFirstHelpDocClassifyByParentId 获取当前父级分类下的排序第一条的数据
+func GetFirstHelpDocClassifyByParentId(parentId int) (item *HelpDocClassify, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM help_doc_classify WHERE parent_id=? order by sort asc,classify_id asc limit 1`
+	err = o.Raw(sql, parentId).QueryRow(&item)
+	return
+}
+
+// UpdateHelpDocClassifySortByParentId 根据父类id更新排序
+func UpdateHelpDocClassifySortByParentId(parentId, classifyId, nowSort int, updateSort string) (err error) {
+	o := orm.NewOrm()
+	sql := ` update help_doc_classify set sort = ` + updateSort + ` WHERE parent_id=? and sort > ? `
+	if classifyId > 0 {
+		sql += ` or ( classify_id > ` + fmt.Sprint(classifyId) + ` and sort= ` + fmt.Sprint(nowSort) + `)`
+	}
+	_, err = o.Raw(sql, parentId, nowSort).Exec()
+	return
+}
+
+// Update 更新分类基础信息
+func (HelpDocClassify *HelpDocClassify) Update(cols []string) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Update(HelpDocClassify, cols...)
+	return
+}
+
+// GetHelpDocClassifyMaxSort 获取分类下最大的排序数
+func GetHelpDocClassifyMaxSort(parentId int) (sort int, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT Max(sort) AS sort FROM help_doc_classify WHERE parent_id=?  `
+	err = o.Raw(sql, parentId).QueryRow(&sort)
+	return
+}
+
+type HelpDocClassifyView struct {
+	HelpDocClassifyId   int    `orm:"column(classify_id);pk"`
+	HelpDocClassifyName string `description:"分类名称"`
+	ParentId            int    `description:"父级id"`
+}
+
+func EditHelpDocClassifySysUser(classifyId, sysUserId int, HelpDocClassifyName string) (err error) {
+	o := orm.NewOrm()
+	sql := `UPDATE help_doc_classify SET sys_user_id=?,sys_user_real_name=?,modify_time=NOW() WHERE parent_id=?  AND level=3 `
+	_, err = o.Raw(sql, sysUserId, HelpDocClassifyName, classifyId).Exec()
+	return
+}
+
+func GetHelpDocClassifyChildCounts(parentId int) (count int, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT COUNT(1) AS count FROM help_doc_classify WHERE parent_id=? `
+	err = o.Raw(sql, parentId).QueryRow(&count)
+	return
+}
+
+func GetHelpDocCounts(classifyId int) (count int, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT COUNT(1) AS count FROM help_doc WHERE classify_id=? `
+	err = o.Raw(sql, classifyId).QueryRow(&count)
+	return
+}
+
+// DeleteHelpDocClassify 删除分类
+func DeleteHelpDocClassify(classifyId int) (err error) {
+	o := orm.NewOrm()
+	sql := ` DELETE FROM help_doc_classify WHERE classify_id =? `
+	_, err = o.Raw(sql, classifyId).Exec()
+	return
+}
+
+// MoveClassifyReq 移动分类请求参数
+type MoveClassifyReq struct {
+	ClassifyId       int `description:"分类id"`
+	ParentClassifyId int `description:"父级分类id"`
+	PrevClassifyId   int `description:"上一个兄弟节点分类id"`
+	NextClassifyId   int `description:"下一个兄弟节点分类id"`
+}
+
+// GetHelpDocClassifyLevel
+func GetHelpDocClassifyLevel() (items []*HelpDocClassifyItems, err error) {
+	o := orm.NewOrm()
+	sql := ``
+	sql = ` SELECT * FROM help_doc_classify WHERE level IN (1,2) `
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}
+
+type EditHelpDocClassifyVisibleReq struct {
+	HelpDocClassifyId  int `description:"分类id"`
+	VisibleBusinessIds string
+}
+
+func EditHelpDocClassifyVisible(classifyId int, visibleBusinessIds string) (err error) {
+	o := orm.NewOrm()
+	sql := `UPDATE help_doc_classify SET visible_business_ids=?, modify_time=NOW() WHERE classify_id=? `
+	_, err = o.Raw(sql, visibleBusinessIds, classifyId).Exec()
+	return
+}
+
+func AddAllHelpDocClassifyVisible(businessId int) (err error) {
+	o := orm.NewOrm()
+	sql := `UPDATE help_doc_classify set visible_business_ids=CONCAT(visible_business_ids,",", ?)  where visible_business_ids !=""`
+	_, err = o.Raw(sql, businessId).Exec()
+	if err != nil {
+		return
+	}
+	sql = `UPDATE help_doc_classify set visible_business_ids=CONCAT(visible_business_ids, ?)  where visible_business_ids =""`
+	_, err = o.Raw(sql, businessId).Exec()
+	return
+}
+
+func GetAllHelpDocClassify() (items []*HelpDocClassifyItems, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM help_doc_classify `
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}
+
+func GetAllHelpDocClassifyByKeyword(keyword string) (items []*HelpDocClassifyItems, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM help_doc_classify WHERE classify_name LIKE '%` + keyword + `%' `
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}

+ 198 - 0
models/help_doc/help_doc.go

@@ -0,0 +1,198 @@
+package help_doc
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"time"
+)
+
+type HelpDoc struct {
+	Id            int       `orm:"column(id);pk"`
+	ClassifyId    int       // 分类id
+	Title         string    // 标题
+	Author        string    // 作者
+	CreateTime    time.Time // 创建时间
+	ModifyTime    time.Time // 修改时间
+	Status        int       // 1:未发布,2:已发布
+	PublishTime   time.Time // 发布时间
+	Content       string    // 内容
+	AdminId       int       // 创建人
+	AdminRealName string    // 创建人姓名
+	Anchor        string    // 锚点
+	Recommend     string    // 推荐
+}
+
+type HelpDocItem struct {
+	Id            int    `orm:"column(id);pk"`
+	ClassifyId    int    // 分类id
+	Title         string // 标题
+	Author        string // 作者
+	CreateTime    string // 创建时间
+	ModifyTime    string // 修改时间
+	Status        int    // 1:未发布,2:已发布
+	PublishTime   string // 发布时间
+	Content       string // 内容
+	AdminId       int    // 创建人
+	AdminRealName string // 创建人姓名
+	Anchor        string // 锚点
+	Recommend     string // 推荐
+}
+
+func EditHelpDocClassifyId(classifyId int, classifyName string) (err error) {
+	o := orm.NewOrm()
+	sql := `UPDATE help_doc SET classify_name=?,modify_time=NOW() WHERE classify_id=? `
+	_, err = o.Raw(sql, classifyId, classifyName).Exec()
+	return
+}
+
+type AddHelpDocReq struct {
+	Id            int64  `description:"文章id"`
+	ClassifyId    int    `description:"分类id"`
+	Title         string `description:"标题"`
+	Author        string `description:"作者"`
+	Status        int    `description:"状态:1:未发布,2:已发布"`
+	Content       string `description:"内容"`
+	IsChange      bool   `description:"内容是否改变"`
+	AnchorData    []AnchorList
+	RecommendData []RecommendList
+}
+
+type AnchorList struct {
+	AnchorId   string
+	Anchor     string
+	AnchorName string
+	Child      []AnchorList
+}
+
+type RecommendList struct {
+	Name string
+	Url  string
+}
+
+func AddHelpDoc(item *HelpDoc) (lastId int64, err error) {
+	o := orm.NewOrm()
+	lastId, err = o.Insert(item)
+	return
+}
+
+type EditHelpDocReq struct {
+	Id            int64  `description:"文章id"`
+	ClassifyId    int    `description:"分类id"`
+	Title         string `description:"标题"`
+	Author        string `description:"作者"`
+	Status        int    `description:"状态:1:未发布,2:已发布"`
+	Content       string `description:"内容"`
+	AnchorData    []AnchorList
+	RecommendData []RecommendList
+}
+
+func EditHelpDoc(item *HelpDoc, reportId int64) (err error) {
+	o := orm.NewOrm()
+	sql := `UPDATE help_doc
+			SET
+			  classify_id =?,
+			  title = ?,
+			  author = ?,
+			  content = ?,
+			  modify_time = ?,
+			  status = ?,
+			  publish_time = ?,
+			  anchor = ?,
+			  recommend = ? 
+			WHERE id = ? `
+	_, err = o.Raw(sql, item.ClassifyId, item.Title, item.Author, item.Content, item.ModifyTime,
+		item.Status, item.PublishTime, item.Anchor, item.Recommend, reportId).Exec()
+	return
+}
+
+func GetHelpDocById(docId int) (item *HelpDocItem, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM help_doc WHERE id=?`
+	err = o.Raw(sql, docId).QueryRow(&item)
+	return
+}
+
+type HelpDocResp struct {
+	Id            int             `orm:"column(id);pk"`
+	ClassifyId    int             // 分类id
+	ClassifyName  string          // 分类路径
+	Title         string          // 标题
+	Author        string          // 作者
+	CreateTime    string          // 创建时间
+	ModifyTime    string          // 修改时间
+	Status        int             // 1:未发布,2:已发布
+	PublishTime   string          // 发布时间
+	Content       string          // 内容
+	AdminId       int             // 创建人
+	AdminRealName string          // 创建人姓名
+	Anchor        []AnchorList    // 锚点
+	Recommend     []RecommendList // 推荐
+}
+
+func GetHelpDocListCount(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT COUNT(1) AS count FROM help_doc WHERE 1=1 `
+	if condition != "" {
+		sql += condition
+	}
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+func GetHelpDocList(condition string, pars []interface{}, startSize, pageSize int) (items []*HelpDocItem, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM help_doc WHERE 1=1 `
+	if condition != "" {
+		sql += condition
+	}
+	sql += `ORDER BY create_time DESC LIMIT ?,?`
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+type HelpDocListResp struct {
+	List         []*HelpDocResp
+	ClassifyName string
+	Paging       *paging.PagingItem `description:"分页数据"`
+}
+
+type PublishReq struct {
+	DocId  int
+	Status int `description:"状态:1:未发布,2:已发布"`
+}
+
+// 发布报告
+func PublishHelpDocById(reportId, status int) (err error) {
+	o := orm.NewOrm()
+	sql := ``
+	if status == 1 {
+		sql = `UPDATE help_doc SET status=1,publish_time=NULL,modify_time=NOW() WHERE id = ? `
+	} else {
+		sql = `UPDATE help_doc SET status=2,publish_time=now(),modify_time=NOW() WHERE id = ? `
+	}
+	_, err = o.Raw(sql, reportId).Exec()
+	return
+}
+
+type DeleteReq struct {
+	DocId int `description:"id"`
+}
+
+func DeleteHelpDoc(reportIds int) (err error) {
+	o := orm.NewOrm()
+	sql := ` DELETE FROM help_doc WHERE id =? `
+	_, err = o.Raw(sql, reportIds).Exec()
+	return
+}
+
+func GetAllHelpDoc() (items []*HelpDocItem, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM help_doc `
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}
+
+type HelpDocSaveResp struct {
+	HelpDocId  int64  `description:"智能研报ID"`
+	ModifyTime string `description:"修改时间"`
+}

+ 182 - 0
models/system/admin.go

@@ -189,3 +189,185 @@ type BusinessAdmin struct {
 type BusinessAdminResp struct {
 	List []*BusinessAdmin `description:"用户列表"`
 }
+
+func GetSysAdminById(adminId int) (item *Admin, err error) {
+	sql := `SELECT * FROM admin WHERE admin_id=? `
+	o := orm.NewOrm()
+	err = o.Raw(sql, adminId).QueryRow(&item)
+	return
+}
+
+func GetSysUserItems(condition string, pars []interface{}) (items []*Admin, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM admin WHERE 1=1 `
+	if condition != "" {
+		sql += condition
+	}
+	sql += `ORDER BY last_updated_time DESC `
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+type HzEtaBusiness struct {
+	BusinessName string `description:"商家名称"`
+	BusinessCode string
+}
+
+func GetSysUserItemsOrderByCreated(condition string, pars []interface{}) (items []*Admin, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM admin WHERE 1=1 `
+	if condition != "" {
+		sql += condition
+	}
+	sql += ` ORDER BY created_time DESC `
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+// GetTeamSysUserList 根据小组id获取系统用户列表
+func GetTeamSysUserList(teamId, enabled int) (items []*Admin, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM admin WHERE group_id=? `
+	if enabled >= 0 {
+		sql += fmt.Sprint(` AND enabled=`, enabled, ` `)
+	}
+	sql += ` ORDER BY created_time DESC `
+	_, err = o.Raw(sql, teamId).QueryRows(&items)
+	return
+}
+
+// GetGrooupsysUserList 根据大组id和小组id获取系统用户列表
+func GetGrooupsysUserList(groupId int, roleTypeCodes string, enabled int) (items []*Admin, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM admin WHERE group_id = ? `
+	if roleTypeCodes != "" {
+		sql += ` and role_type_code in (` + roleTypeCodes + `) `
+	}
+	if enabled >= 0 {
+		sql += fmt.Sprint(` AND enabled=`, enabled, ` `)
+	}
+	sql += ` ORDER BY created_time asc `
+	_, err = o.Raw(sql, groupId).QueryRows(&items)
+	return
+}
+
+// GetDepartmentGroupSysUserList 根据部门id和分组id获取系统用户列表
+func GetDepartmentGroupSysUserList(departmentId, groupId int, roleTypeCodes string) (items []*Admin, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM admin WHERE department_id = ? and group_id=? and enabled=1 `
+	if roleTypeCodes != "" {
+		sql += ` and role_type_code in (` + roleTypeCodes + `) `
+	}
+	sql += ` ORDER BY created_time asc `
+	_, err = o.Raw(sql, departmentId, groupId).QueryRows(&items)
+	return
+}
+
+// GetGroupSysUserList 根据分组id获取系统用户列表
+func GetGroupSysUserList(groupId int) (items []*Admin, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM admin WHERE group_id=? ORDER BY created_time DESC `
+	_, err = o.Raw(sql, groupId).QueryRows(&items)
+	return
+}
+
+type DifferentAdmin struct {
+	AdminId     int    `description:"系统用户id"`
+	RealName    string `description:"用户真实名称"`
+	Mobile      string `description:"用户手机号"`
+	CrmAdminId  int    `description:"crm系统用户id"`
+	CrmRealName string `description:"crm用户真实名称"`
+	CrmMobile   string `description:"crm用户手机号"`
+}
+
+// GetDifferentAdmin 获取系统用户和crm系统用户不同的用户
+func GetDifferentAdmin() (items []*DifferentAdmin, err error) {
+	sql := `SELECT a.admin_id, a.real_name,  a.mobile, c.admin_id as crm_admin_id, c.real_name as crm_real_name, c.mobile as crm_mobile FROM admin as a LEFT JOIN admin_crm c on a.admin_name= c.admin_name WHERE a.admin_id != c.admin_id `
+	o := orm.NewOrm()
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}
+
+// 初始化,把原先crm中的adminId改成社区里的adminId
+func SetDifferentAdmin(adminId int, adminRealName string, crmAdminId int, crmAdminRealName string, now time.Time, beforeTime string) (err error) {
+	// 事务更新
+	fmt.Println("开始替换 adminId:", adminId, "adminRealName:", adminRealName, "crmAdminId:", crmAdminId, "crmAdminRealName:", crmAdminRealName, "now:", now, "beforeTime:", beforeTime)
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	sql := "UPDATE eta_business SET seller_id = ?, seller_name=?, modify_time=? WHERE seller_id = ? and seller_name = ? and create_time<?"
+	_, err = to.Raw(sql, adminId, adminRealName, now, crmAdminId, crmAdminRealName, beforeTime).Exec()
+	if err != nil {
+		return
+	}
+	sql = "UPDATE eta_business_operation_record SET seller_id = ? WHERE seller_id = ?  and create_time<?"
+	_, err = to.Raw(sql, adminId, crmAdminId, beforeTime).Exec()
+
+	if err != nil {
+		return
+	}
+	sql = "UPDATE eta_business_operation_record SET sys_user_id = ?, sys_real_name=? WHERE sys_user_id = ? and	sys_real_name =?  and create_time<?"
+	_, err = to.Raw(sql, adminId, adminRealName, crmAdminId, crmAdminRealName, beforeTime).Exec()
+	if err != nil {
+		return
+	}
+
+	sql = "UPDATE eta_business_remind_record SET seller_id = ?, seller_name=? WHERE seller_id = ? and seller_name = ? and create_time<?"
+	_, err = to.Raw(sql, adminId, adminRealName, crmAdminId, crmAdminRealName, beforeTime).Exec()
+	if err != nil {
+		return
+	}
+
+	sql = "UPDATE eta_training_video_classify SET sys_user_id = ?, sys_real_name=?, modify_time=? WHERE sys_user_id = ? and	sys_real_name =? and create_time<? "
+	_, err = to.Raw(sql, adminId, adminRealName, now, crmAdminId, crmAdminRealName, beforeTime).Exec()
+	if err != nil {
+		return
+	}
+
+	sql = "UPDATE eta_training_video_op_log SET sys_user_id = ?, sys_real_name=? WHERE sys_user_id = ? and	sys_real_name =?  and create_time<?"
+	_, err = to.Raw(sql, adminId, adminRealName, crmAdminId, crmAdminRealName, beforeTime).Exec()
+	if err != nil {
+		return
+	}
+
+	sql = "UPDATE eta_training_video_tag SET sys_user_id = ?, sys_real_name=?, modify_time=? WHERE sys_user_id = ? and	sys_real_name =?  and create_time<?"
+	_, err = to.Raw(sql, adminId, adminRealName, now, crmAdminId, crmAdminRealName, beforeTime).Exec()
+	if err != nil {
+		return
+	}
+
+	sql = "UPDATE eta_trial SET seller_id = ?, seller=?, modify_time=? WHERE seller_id = ? and seller = ? and create_time<?"
+	_, err = to.Raw(sql, adminId, adminRealName, now, crmAdminId, crmAdminRealName, beforeTime).Exec()
+	if err != nil {
+		return
+	}
+
+	sql = "UPDATE eta_trial_approval SET seller_id = ?, seller=?, modify_time=? WHERE seller_id = ? and seller = ? and create_time<?"
+	_, err = to.Raw(sql, adminId, adminRealName, now, crmAdminId, crmAdminRealName, beforeTime).Exec()
+	if err != nil {
+		return
+	}
+
+	sql = "UPDATE help_doc SET admin_id = ?, admin_real_name=?, modify_time=? WHERE admin_id = ? and admin_real_name = ? and create_time<?"
+	_, err = to.Raw(sql, adminId, adminRealName, now, crmAdminId, crmAdminRealName, beforeTime).Exec()
+	if err != nil {
+		return
+	}
+
+	sql = "UPDATE help_doc_classify SET sys_user_id = ?, sys_user_real_name=?, modify_time=? WHERE sys_user_id = ? and	sys_user_real_name =?  and create_time<?"
+	_, err = to.Raw(sql, adminId, adminRealName, now, crmAdminId, crmAdminRealName, beforeTime).Exec()
+	if err != nil {
+		return
+	}
+
+	return
+}

+ 132 - 0
models/system/crm_config.go

@@ -0,0 +1,132 @@
+package system
+
+import (
+	"encoding/json"
+	"errors"
+	"eta/eta_forum_admin/utils"
+	"github.com/beego/beego/v2/client/orm"
+)
+
+const (
+	ConfAreaCodeListKey         = "area_code_list" // 手机号区号列表
+	CrmConfigLoginSmsTpId       = "LoginSmsTpId"
+	CrmConfigLoginSmsGjTpId     = "LoginSmsGjTpId"
+	CrmConfigSmsJhgnAppKey      = "SmsJhgnAppKey"
+	CrmConfigSmsJhgjAppKey      = "SmsJhgjAppKey"
+	CrmConfigLoginSmsTplContent = "LoginSmsTplContent"
+	CrmConfigSmsJhgjVariable    = "SmsJhgjVariable" // 聚合国际短信变量
+	CrmConfigICPLicense         = "ICPLicense"
+	CrmConfigLogoCN             = "LogoCN"
+	CrmConfigLogoCNMini         = "LogoCNMini"
+	CrmConfigCompanyWatermark   = "CompanyWatermark"
+)
+
+type CrmConfig struct {
+	ConfigCode  string `description:"详情Code"`
+	ConfigValue string `description:"详情"`
+	IsShow      int    `description:"是否在配置页面展示,1展示,0 不展示"`
+	ValType     int    `description:"1-字符串;2-数值;3-字符串数组;4-富文本;"`
+	Necessary   int    `description:"是否必填:0-否;1-是"`
+	Remark      string `description:"备注"`
+}
+
+func GetConfigValueByCode(configCode string) (total int, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT config_value FROM crm_config WHERE config_code=? `
+	err = o.Raw(sql, configCode).QueryRow(&total)
+	return
+}
+
+type ConfUpdateItem struct {
+	ConfigCode  string `description:"详情Code"`
+	ConfigValue string `description:"详情"`
+}
+
+// 修改
+func CrmConfigUpdate(newValue, configCode string) (err error) {
+	o := orm.NewOrm()
+	sql := `UPDATE crm_config SET  config_value=?   WHERE config_code=  ?`
+	_, err = o.Raw(sql, newValue, configCode).Exec()
+	return
+}
+
+// ConfigClassifyId
+// @Description: 后台配置的报告id
+type ConfigClassifyId struct {
+	Debug   int `json:"debug"`
+	Release int `json:"release"`
+}
+
+func GetCrmConfigDetailByCode(configCode string) (item CrmConfig, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM crm_config WHERE config_code=? `
+	err = o.Raw(sql, configCode).QueryRow(&item)
+	return
+}
+
+// GetReportClassifyIdByConfigKey
+// @Description: 获取关联的报告id
+// @author: Roc
+// @datetime 2024-06-18 14:10:27
+// @param configKey string
+// @return classifyId int
+// @return err error
+func GetReportClassifyIdByConfigKey(configKey string) (classifyId int, err error) {
+	// 别问为啥要从配置里拿=_=!
+	conf, e := GetCrmConfigDetailByCode(configKey)
+	if e != nil {
+		err = errors.New("获取配置的id失败, Err: " + e.Error())
+		return
+	}
+	if conf.ConfigValue == "" {
+		err = errors.New("ID配置有误")
+		return
+	}
+	type TwoWeekIdConf struct {
+		Debug   []int
+		Release []int
+	}
+	classifyIdConf := new(ConfigClassifyId)
+	if e = json.Unmarshal([]byte(conf.ConfigValue), &classifyIdConf); e != nil {
+		err = errors.New("解析ID配置失败, Err: " + e.Error())
+		return
+	}
+	if utils.RunMode == "debug" {
+		classifyId = classifyIdConf.Debug
+	} else {
+		classifyId = classifyIdConf.Release
+	}
+
+	return
+}
+
+type ResourceResp struct {
+	ResourceUrl string `description:"资源地址"`
+}
+
+// GetCrmConfig 获取基础配置
+func GetCrmConfig() (list []*CrmConfig, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM crm_config`
+	_, err = o.Raw(sql).QueryRows(&list)
+	return
+}
+
+// UpdateCrmConfigMulti 批量修改配置
+func UpdateCrmConfigMulti(items []ConfUpdateItem) (err error) {
+	o := orm.NewOrm()
+	p, err := o.Raw("UPDATE crm_config SET config_value = ? WHERE config_code = ?").Prepare()
+	if err != nil {
+		return
+	}
+	defer func() {
+		_ = p.Close()
+	}()
+	for _, v := range items {
+		_, err = p.Exec(v.ConfigValue, v.ConfigCode)
+		if err != nil {
+			return
+		}
+	}
+	return
+}

+ 2 - 9
models/system/eta_business.go

@@ -1,14 +1,6 @@
 package system
 
-import (
-	"eta/eta_forum_admin/utils"
-	"fmt"
-	"github.com/beego/beego/v2/client/orm"
-	"github.com/rdlucklib/rdluck_tools/paging"
-	"strings"
-	"time"
-)
-
+/*
 const (
 	EtaBusinessSigningStatusFirst = iota + 1
 	EtaBusinessSigningStatusContinue
@@ -242,3 +234,4 @@ type EtaBusinessItem struct {
 	CreateTime       string `description:"创建时间"`
 	ModifyTime       string `description:"更新时间"`
 }
+*/

+ 147 - 0
models/system/sys_department.go

@@ -0,0 +1,147 @@
+package system
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+type SysDepartmentAddReq struct {
+	DepartmentName string `description:"部门名称"`
+}
+
+type SysDepartment struct {
+	DepartmentId   int       `orm:"column(department_id);pk" description:"部门Id"`
+	DepartmentName string    `description:"部门名称"`
+	Sort           int       `description:"排序"`
+	CreateTime     time.Time `description:"创建时间"`
+}
+
+func GetSysDepartmentCount(departmentName string) (count int, err error) {
+	sql := `SELECT COUNT(1) AS count FROM sys_department WHERE department_name=? `
+	o := orm.NewOrm()
+	err = o.Raw(sql, departmentName).QueryRow(&count)
+	return
+}
+
+func AddSysDepartment(item *SysDepartment) (lastId int64, err error) {
+	o := orm.NewOrm()
+	lastId, err = o.Insert(item)
+	return
+}
+
+type SysDepartmentEditReq struct {
+	DepartmentId   int    `description:"部门Id"`
+	DepartmentName string `description:"部门名称"`
+}
+
+func GetSysDepartmentById(departmentId int) (item *SysDepartment, err error) {
+	sql := `SELECT * FROM sys_department WHERE department_id=? `
+	o := orm.NewOrm()
+	err = o.Raw(sql, departmentId).QueryRow(&item)
+	return
+}
+
+func GetSysDepartmentByName(departmentName string) (item *SysDepartment, err error) {
+	sql := `SELECT * FROM sys_department WHERE department_name=? `
+	o := orm.NewOrm()
+	err = o.Raw(sql, departmentName).QueryRow(&item)
+	return
+}
+
+func ModifySysDepartment(departmentName string, departmentId int) (err error) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	sql := `UPDATE sys_department SET department_name=? WHERE department_id=? `
+	_, err = to.Raw(sql, departmentName, departmentId).Exec()
+	if err != nil {
+		return
+	}
+	sql = `UPDATE admin SET department_name=? WHERE department_id=? `
+	_, err = to.Raw(sql, departmentName, departmentId).Exec()
+	return
+}
+
+type SysDepartmentDeleteReq struct {
+	DepartmentId int `description:"部门Id"`
+}
+
+func DeleteSysDepartment(departmentId int) (err error) {
+	sql := `DELETE FROM sys_department WHERE department_id=? `
+	o := orm.NewOrm()
+	_, err = o.Raw(sql, departmentId).Exec()
+	return
+}
+
+type SysDepartmentList struct {
+	DepartmentId   int             `orm:"column(department_id);pk" description:"部门Id"`
+	DepartmentName string          `description:"部门名称"`
+	CreateTime     time.Time       `description:"创建时间"`
+	Child          []*SysGroupList `description:"分组"`
+	IsDepartment   bool            `description:"true:部门,false:分组"`
+}
+
+func GetDepartmentList() (items []*SysDepartmentList, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM sys_department ORDER BY sort ASC, create_time ASC `
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}
+
+// GetDepartmentListByIds 根据部门id集合获取部门列表信息
+func GetDepartmentListByIds(departmentIds string) (items []*SysDepartmentList, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM sys_department where 1 = 1 `
+	if departmentIds != "" {
+		sql += `and department_id in(` + departmentIds + `) `
+	}
+	sql += ` ORDER BY sort ASC, create_time ASC `
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}
+
+type SysDepartmentListResp struct {
+	List []*SysDepartmentList
+}
+
+func GetSysDepartmentAll() (item []*SysDepartment, err error) {
+	sql := `SELECT * FROM sys_department ORDER BY sort ASC, department_id ASC `
+	o := orm.NewOrm()
+	_, err = o.Raw(sql).QueryRows(&item)
+	return
+}
+
+type DepartmentSort struct {
+	DepartmentId int
+	Sort         int
+}
+
+func MultiUpdateDepartmentSort(items []*DepartmentSort) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	o := orm.NewOrm()
+	p, err := o.Raw("UPDATE sys_department SET sort = ? WHERE department_id = ?").Prepare()
+	if err != nil {
+		return
+	}
+	defer func() {
+		_ = p.Close()
+	}()
+	for _, v := range items {
+		_, err = p.Exec(v.Sort, v.DepartmentId)
+		if err != nil {
+			return
+		}
+	}
+	return
+}

+ 254 - 0
models/system/sys_group.go

@@ -0,0 +1,254 @@
+package system
+
+import (
+	"eta/eta_forum_admin/utils"
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+type SysGroupAddReq struct {
+	DepartmentId int    `description:"部门Id"`
+	GroupName    string `description:"分组名称,多个用英文逗号隔开"`
+}
+
+type SysGroup struct {
+	GroupId      int       `orm:"column(group_id);pk" description:"分组ID"`
+	DepartmentId int       `description:"部门Id"`
+	ParentId     int       `description:"父级Id"`
+	GroupName    string    `description:"分组名称"`
+	Sort         int       `description:"排序"`
+	CreateTime   time.Time `description:"创建时间"`
+}
+
+func GetSysGroupCount(departmentId int, groupName string) (count int, err error) {
+	sql := `SELECT COUNT(1) AS count FROM sys_group WHERE department_id=? AND group_name=? `
+	o := orm.NewOrm()
+	err = o.Raw(sql, departmentId, groupName).QueryRow(&count)
+	return
+}
+
+func AddSysGroup(item *SysGroup) (lastId int64, err error) {
+	o := orm.NewOrm()
+	lastId, err = o.Insert(item)
+	return
+}
+
+type SysGroupEditReq struct {
+	GroupId   int    `description:"分组ID"`
+	GroupName string `description:"分组名称"`
+}
+
+func GetSysGroupById(groupId int) (item *SysGroup, err error) {
+	sql := `SELECT * FROM sys_group WHERE group_id=? `
+	o := orm.NewOrm()
+	err = o.Raw(sql, groupId).QueryRow(&item)
+	return
+}
+
+func GetSysGroupByName(groupName string) (item *SysGroup, err error) {
+	sql := `SELECT * FROM sys_group WHERE group_name=? `
+	o := orm.NewOrm()
+	err = o.Raw(sql, groupName).QueryRow(&item)
+	return
+}
+
+func ModifySysGroup(groupName string, groupId int) (err error) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	sql := `UPDATE sys_group SET group_name=? WHERE group_id=? `
+	_, err = to.Raw(sql, groupName, groupId).Exec()
+	sql = `UPDATE admin SET group_name=? WHERE group_id=? `
+	_, err = to.Raw(sql, groupName, groupId).Exec()
+	return
+}
+
+type SysGroupDeleteReq struct {
+	GroupId int `description:"分组ID"`
+}
+
+func DeleteSysGroup(groupId int) (err error) {
+	sql := `DELETE FROM sys_group WHERE group_id=? `
+	o := orm.NewOrm()
+	_, err = o.Raw(sql, groupId).Exec()
+	return
+}
+
+// 因前端显示需要,TopId字段用来当做一级部门id,DepartmentId为当前分组id
+type SysGroupList struct {
+	GroupId      int            `orm:"column(group_id);pk" json:"DepartmentId" description:"分组ID"`
+	ParentId     int            `json:"ParentId" description:"父级ID"`
+	DepartmentId int            `json:"TopId" description:"部门Id"`
+	GroupName    string         `json:"DepartmentName" description:"分组名称"`
+	Child        []*SysTeamList `description:"小组"`
+	CreateTime   time.Time      `description:"创建时间"`
+	IsGroup      bool           `description:"是否为二级部门"`
+}
+
+func GetSysGroupByDepartmentId(departmentId int) (items []*SysGroupList, err error) {
+	sql := `SELECT * FROM sys_group WHERE department_id=? AND parent_id=0 ORDER BY sort ASC, create_time ASC`
+	o := orm.NewOrm()
+	_, err = o.Raw(sql, departmentId).QueryRows(&items)
+	return
+}
+
+// GetSysGroupListByDepartmentId 获取该部门下的所有分组(包含大小分组,不包含 “无” 这个分组)
+func GetSysGroupListByDepartmentId(departmentId int) (items []*SysGroupList, err error) {
+	sql := `SELECT * FROM sys_group WHERE department_id=? AND group_name<>'无' ORDER BY sort ASC, create_time ASC`
+	o := orm.NewOrm()
+	_, err = o.Raw(sql, departmentId).QueryRows(&items)
+	return
+}
+
+func ClearSysUserGroup(groupId int) (err error) {
+	sql := `UPDATE admin SET group_id=0,group_name='' WHERE group_id=? `
+	o := orm.NewOrm()
+	_, err = o.Raw(sql, groupId).Exec()
+	return
+}
+
+func GetSysGroupByDirectorId(directorId int) (items []*SysGroupList, err error) {
+	sql := `SELECT * FROM sys_group WHERE group_id=? ORDER BY sort ASC, create_time ASC`
+	o := orm.NewOrm()
+	_, err = o.Raw(sql, directorId).QueryRows(&items)
+	return
+}
+
+func GetSysGroupByGroupIds(groupIds []int) (items []*SysGroupList, err error) {
+	lenArr := len(groupIds)
+	if lenArr == 0 {
+		return
+	}
+	sql := `SELECT * FROM sys_group WHERE group_id IN  (` + utils.GetOrmInReplace(lenArr) + `) ORDER BY sort ASC, create_time ASC`
+	o := orm.NewOrm()
+	_, err = o.Raw(sql, groupIds).QueryRows(&items)
+	return
+}
+
+// GetSysGroupByGroupId 销售主管用,查找销售主管所在大组的名称
+func GetSysGroupByGroupId(groupId int) (items []*SysGroupList, err error) {
+	sql := `SELECT * FROM sys_group WHERE department_id=2 AND parent_id=0 AND group_id=? ORDER BY sort ASC, create_time ASC`
+	o := orm.NewOrm()
+	_, err = o.Raw(sql, groupId).QueryRows(&items)
+	return
+}
+
+// GetChildSysGroupByGroupId 通过上级分组id获取下级的分组id
+func GetChildSysGroupByGroupId(groupId int) (items []*SysGroup, err error) {
+	sql := `SELECT * FROM sys_group WHERE parent_id=? ORDER BY sort ASC, create_time ASC`
+	o := orm.NewOrm()
+	_, err = o.Raw(sql, groupId).QueryRows(&items)
+	return
+}
+
+type SysFullGroup struct {
+	GroupId         int       `orm:"column(group_id);pk" description:"分组ID"`
+	DepartmentId    int       `description:"部门Id"`
+	ParentId        int       `description:"父级Id"`
+	GroupName       string    `description:"分组名称"`
+	ParentGroupName string    `description:"父级分组名称"`
+	DepartmentName  string    `description:"部门名称"`
+	CreateTime      time.Time `description:"创建时间"`
+}
+
+// GetFullGroup 获取完整的分组信息
+func GetFullGroup() (list []*SysFullGroup, err error) {
+	sql := `SELECT s.*,g.group_name as parent_group_name , d.department_name
+from sys_group s 
+LEFT JOIN sys_group g on s.parent_id=g.group_id
+LEFT JOIN sys_department d on s.department_id=d.department_id ORDER BY s.sort ASC, s.create_time ASC`
+	o := orm.NewOrm()
+	_, err = o.Raw(sql).QueryRows(&list)
+	return
+}
+
+type SysGroupSortReq struct {
+	//ParentId      int   `description:"上级部门/分组ID"`
+	DepartmentIds []int `description:"移动后的一级ID排序"`
+	GroupIds      []int `description:"移动后的二级ID排序"`
+	TeamIds       []int `description:"移动后的三级ID排序"`
+}
+
+type GroupSort struct {
+	GroupId int
+	Sort    int
+}
+
+func MultiUpdateGroupSort(items []*GroupSort) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	o := orm.NewOrm()
+	p, err := o.Raw("UPDATE sys_group SET sort = ? WHERE group_id = ?").Prepare()
+	if err != nil {
+		return
+	}
+	defer func() {
+		_ = p.Close()
+	}()
+	for _, v := range items {
+		_, err = p.Exec(v.Sort, v.GroupId)
+		if err != nil {
+			return
+		}
+	}
+	return
+}
+
+func GetGroupByDepartmentId(departmentId int) (list []*SysFullGroup, err error) {
+	sql := `SELECT
+				s.*, g.group_name AS parent_group_name,
+				d.department_name
+			FROM
+				sys_group s
+			LEFT JOIN sys_group g ON s.parent_id = g.group_id
+			LEFT JOIN sys_department d ON s.department_id = d.department_id
+			WHERE
+				s.department_id = ?
+			ORDER BY
+				s.sort ASC,
+				s.create_time ASC`
+	o := orm.NewOrm()
+	_, err = o.Raw(sql, departmentId).QueryRows(&list)
+	return
+}
+
+type RoadshowGroupResp struct {
+	List []RoadshowGroups
+}
+
+type RoadshowGroups struct {
+	GroupId   int                    `description:"分组ID"`
+	GroupName string                 `description:"分组名称"`
+	Child     []RoadshowGroupSellers `description:"销售"`
+}
+
+type RoadshowGroupSellers struct {
+	GroupId   int    `description:"大组ID"`
+	TeamId    int    `description:"小组ID"`
+	AdminId   int    `description:"销售ID"`
+	AdminName string `description:"销售名称"`
+}
+
+func GetAllSysGroupByGroupId(groupId int) (items []*SysGroup, err error) {
+	sql := `SELECT * FROM sys_group WHERE parent_id=? OR parent_id = ? ORDER BY sort ASC, create_time ASC`
+	o := orm.NewOrm()
+	_, err = o.Raw(sql, groupId, groupId).QueryRows(&items)
+	return
+}
+
+func GetParentIdFromGroup(gid int) (items *int, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT parent_id FROM sys_group WHERE group_id=? `
+	err = o.Raw(sql, gid).QueryRow(&items)
+	return
+}

+ 214 - 0
models/system/sys_menu.go

@@ -0,0 +1,214 @@
+package system
+
+import (
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"strings"
+	"time"
+)
+
+// 需要特殊处理的菜单字段项
+const (
+	MenuSpecialHandleClassifyChildMenu  = "classifyList:cnClassify:childMenu"
+	MenuSpecialHandleClassifyShowType   = "classifyList:cnClassify:showType"
+	MenuSpecialHandleClassifyReportImgs = "classifyList:cnClassify:reportImgs"
+	MenuSpecialHandleSandboxVariety     = "sandbox:variety"
+)
+
+type SysMenu struct {
+	MenuId     int       `orm:"column(menu_id);pk"`
+	ParentId   int       `description:"父级菜单ID"`
+	Name       string    `description:"菜单名称或者按钮名称"`
+	Sort       string    `description:"排序"`
+	Path       string    `description:"路由地址"`
+	IconPath   string    `description:"菜单图标地址"`
+	Component  int       `description:"组件路径"`
+	Hidden     int       `description:"是否隐藏:1-隐藏 0-显示"`
+	IsLevel    int       `description:"是否为多级菜单:1,只有一级;2,有多级"`
+	LevelPath  string    `description:"兼容以前menu表的字段"`
+	MenuType   int       `description:"菜单类型: 0-菜单; 1-按钮; 2-字段(需要特殊处理)"`
+	ButtonCode string    `description:"按钮/菜单唯一标识"`
+	CreateTime time.Time `description:"创建时间"`
+	ModifyTime time.Time `description:"更新时间"`
+	Api        string    `description:"按钮相关api"`
+	NameEn     string    `description:"菜单名称或者按钮名称(英文)"`
+}
+
+// GetSysMenuItemsByCondition 获取菜单列表
+func GetSysMenuItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*SysMenu, err error) {
+	o := orm.NewOrm()
+	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 sys_menu WHERE 1=1 %s %s`, fields, condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+type MenuList struct {
+	MenuId    int          `description:"导航唯一标识"`
+	IsLevel   int          `description:"1,只有一级;2,有多级"`
+	Name      string       `json:"name" description:"导航名称"`
+	NameEn    string       `json:"name_en" description:"导航名称(英文)"`
+	Path      string       `json:"path"`
+	IconPath  string       `json:"icon_path"`
+	LevelPath string       `json:"level_path"`
+	Component string       `json:"component"`
+	Hidden    bool         `json:"hidden"`
+	Children  []*ChildMenu `json:"children"`
+}
+
+type ChildMenu struct {
+	MenuId    int    `description:"导航唯一标识"`
+	Name      string `json:"name" description:"导航名称"`
+	NameEn    string `json:"name_en" description:"导航名称(英文)"`
+	Path      string `json:"path"`
+	Component string `json:"component"`
+	IconPath  string `json:"icon_path"`
+	Hidden    bool   `json:"hidden"`
+}
+
+type MenuListResp struct {
+	List []*MenuList
+}
+
+func GetMenuList(roleId int) (items []*MenuList, err error) {
+	sql := ` SELECT a.* FROM sys_menu AS a
+			INNER JOIN sys_role_menu AS b ON a.menu_id=b.menu_id
+			INNER JOIN sys_role AS c ON b.role_id=c.role_id
+			WHERE c.role_id=?
+			AND a.parent_id=0 
+            ORDER BY sort ASC `
+	_, err = orm.NewOrm().Raw(sql, roleId).QueryRows(&items)
+	return
+}
+
+func GetMenuByParentId(roleId, parentId int) (items []*ChildMenu, err error) {
+	sql := ` SELECT a.* FROM sys_menu AS a
+			INNER JOIN sys_role_menu AS b ON a.menu_id=b.menu_id
+			INNER JOIN sys_role AS c ON b.role_id=c.role_id
+			WHERE c.role_id=?
+			AND a.parent_id=? 
+            ORDER BY sort ASC `
+	_, err = orm.NewOrm().Raw(sql, roleId, parentId).QueryRows(&items)
+	return
+}
+
+// GetMenuListByRoleIds 根据管理员多个角色查询菜单ID
+func GetMenuListByRoleIds(roleIds string) (items []*MenuList, err error) {
+	sql := ` SELECT DISTINCT a.* FROM sys_menu AS a
+			INNER JOIN sys_role_menu AS b ON a.menu_id=b.menu_id
+			INNER JOIN sys_role AS c ON b.role_id=c.role_id
+			WHERE c.role_id in (` + roleIds + `)
+			AND a.parent_id=0 
+            ORDER BY sort ASC `
+	_, err = orm.NewOrm().Raw(sql).QueryRows(&items)
+	return
+}
+
+// GetMenuByParentIdRoleIds 根据管理员多个角色查询子菜单ID
+func GetMenuByParentIdRoleIds(roleIds string, parentId int) (items []*ChildMenu, err error) {
+	sql := ` SELECT DISTINCT a.* FROM sys_menu AS a
+			INNER JOIN sys_role_menu AS b ON a.menu_id=b.menu_id
+			INNER JOIN sys_role AS c ON b.role_id=c.role_id
+			WHERE c.role_id in (` + roleIds + `)
+			AND a.parent_id=? 
+            ORDER BY a.sort ASC, create_time DESC, menu_id DESC`
+	_, err = orm.NewOrm().Raw(sql, parentId).QueryRows(&items)
+	return
+}
+
+// GetMenuButtonsByRoleId 获取角色按钮菜单
+func GetMenuButtonsByRoleId(roleId int) (items []*SysMenu, err error) {
+	sql := `SELECT
+				r.*
+			FROM
+				sys_menu AS r
+			JOIN sys_role_menu AS rm ON r.menu_id = rm.menu_id AND rm.type = 0
+			WHERE
+				rm.role_id = ? AND r.menu_type <> 0 AND r.hidden = 0
+			ORDER BY
+				r.sort ASC,
+				r.create_time DESC`
+	_, err = orm.NewOrm().Raw(sql, roleId).QueryRows(&items)
+	return
+}
+
+// SysMenuButtonResp 按钮菜单响应体
+type SysMenuButtonResp struct {
+	MenuId     int    `description:"菜单ID"`
+	ParentId   int    `description:"父级菜单ID"`
+	Name       string `description:"菜单名称或者按钮名称"`
+	MenuType   int    `description:"菜单类型: 0-菜单; 1-按钮"`
+	ButtonCode string `description:"按钮唯一标识"`
+}
+
+// SysMenuListResp ETA商家菜单列表响应体
+type SysMenuListResp struct {
+	ChoiceList     []int          `description:"已选菜单"`
+	HalfChoiceList []int          `description:"半选菜单-方便前端回显用的"`
+	List           []*SysMenuItem `description:"菜单列表"`
+}
+
+// SysMenuItem ETA商家菜单
+type SysMenuItem struct {
+	MenuId     int
+	ParentId   int            `description:"父级菜单ID"`
+	Name       string         `description:"菜单名称或者按钮名称"`
+	Sort       string         `description:"排序"`
+	Path       string         `description:"路由地址"`
+	IconPath   string         `description:"菜单图标地址"`
+	Component  int            `description:"组件路径"`
+	Hidden     int            `description:"是否隐藏:1-隐藏 0-显示"`
+	MenuType   int            `description:"菜单类型: 0-菜单; 1-按钮; 2-字段(需要特殊处理)"`
+	ButtonCode string         `description:"按钮/菜单唯一标识"`
+	Children   []*SysMenuItem `description:"子菜单"`
+}
+
+// BusinessConf 商户配置表
+type BusinessConf struct {
+	ConfKey string `description:"配置Key"`
+	ConfVal string `description:"配置值"`
+}
+
+// GetMenuButtonApisByRoleId 获取角色按钮api菜单
+func GetMenuButtonApisByRoleId(roleId int) (items []*SysMenu, err error) {
+	sql := `SELECT
+				r.*
+			FROM
+				sys_menu AS r
+			JOIN sys_role_menu AS rm ON r.menu_id = rm.menu_id 
+			WHERE
+				rm.role_id = ?
+			ORDER BY
+				r.sort ASC,
+				r.create_time DESC`
+	_, err = orm.NewOrm().Raw(sql, roleId).QueryRows(&items)
+	return
+}
+
+// SysMenuSimple ETA商家菜单
+type SysMenuSimple struct {
+	MenuId   int    `orm:"column(menu_id);pk"`
+	ParentId int    `description:"父级菜单ID"`
+	Name     string `description:"菜单名称或者按钮名称"`
+	Sort     string `description:"排序"`
+	Path     string `description:"路由地址"`
+	NameEn   string `description:"菜单名称或者按钮名称(英文)"`
+}
+
+// GetMenuByRoleIds 根据管理员多个角色查询菜单ID
+func GetMenuByRoleIds(roleIds string) (items []*SysMenu, err error) {
+	sql := ` SELECT DISTINCT a.* FROM sys_menu AS a
+			INNER JOIN sys_role_menu AS b ON a.menu_id=b.menu_id AND b.type = 0
+			INNER JOIN sys_role AS c ON b.role_id=c.role_id
+			WHERE c.role_id in (` + roleIds + `)
+            ORDER BY a.sort ASC, create_time DESC, menu_id DESC`
+	_, err = orm.NewOrm().Raw(sql).QueryRows(&items)
+	return
+}

+ 87 - 0
models/system/sys_menu_button.go

@@ -0,0 +1,87 @@
+package system
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+type SysMenuButton struct {
+	MenuButtonId int `orm:"column(menu_button_id);pk" description:"菜单按钮id"`
+	MenuId       int
+	ButtonName   int
+	CreateTime   time.Time
+}
+
+type SysMenuButtonItem struct {
+	MenuButtonId int    `description:"菜单按钮id"`
+	MenuId       int    `description:"菜单id"`
+	ButtonName   string `description:"按钮名称"`
+}
+
+func GetSysMenuButtonItems(menuId int) (items []*SysMenuButtonItem, err error) {
+	sql := ` SELECT * FROM sys_menu_button  WHERE menu_id=? `
+	_, err = orm.NewOrm().Raw(sql, menuId).QueryRows(&items)
+	return
+}
+
+type MenuButtonsResp struct {
+	List      []*ButtonMenu
+	CheckList []*int
+}
+
+func AddSysMenuButton(item *SysMenuButton) (lastId int64, err error) {
+	o := orm.NewOrm()
+	lastId, err = o.Insert(item)
+	return
+}
+
+type SysRoleButton struct {
+	Id           int `orm:"column(id);pk" description:"关联id"`
+	RoleId       int
+	MenuButtonId int
+	CreateTime   time.Time
+}
+
+func AddSysRoleButton(item *SysRoleButton) (lastId int64, err error) {
+	o := orm.NewOrm()
+	lastId, err = o.Insert(item)
+	return
+}
+
+func GetRoleButtonCheck(roleId int) (items []*SysRoleButton, err error) {
+	sql := `SELECT b.* FROM  sys_menu_button AS a
+			INNER JOIN sys_role_button AS b ON a.menu_button_id=b.menu_button_id
+			WHERE b.role_id=?`
+	o := orm.NewOrm()
+	_, err = o.Raw(sql, roleId).QueryRows(&items)
+	return
+}
+
+type ButtonMenu struct {
+	MenuId int    `description:"导航唯一标识"`
+	Name   string `json:"name" description:"导航名称"`
+	List   []*SysMenuButtonItem
+}
+
+func GetButtonMenu() (items []*ButtonMenu, err error) {
+	sql := `SELECT b.menu_id,b.name FROM sys_menu_button AS a
+			INNER JOIN sys_menu AS b ON a.menu_id=b.menu_id
+			GROUP BY  b.menu_id `
+	o := orm.NewOrm()
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}
+
+func DeleteSysRoleButton(roleId int) (err error) {
+	o := orm.NewOrm()
+	sql := `DELETE FROM sys_role_button WHERE role_id=? `
+	_, err = o.Raw(sql, roleId).Exec()
+	return
+}
+
+func GetSysRoleButtonCount(roleId, buttonId int) (count int, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT COUNT(1) AS count FROM sys_role_button WHERE role_id=? AND menu_button_id=? `
+	_, err = o.Raw(sql, roleId, buttonId).Exec()
+	return
+}

+ 269 - 0
models/system/sys_role.go

@@ -0,0 +1,269 @@
+package system
+
+import (
+	"eta/eta_forum_admin/utils"
+	"time"
+
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/rdlucklib/rdluck_tools/paging"
+)
+
+type SysRoleAddReq struct {
+	RoleName string `description:"角色名称"`
+	RoleType string `description:"角色类型"`
+}
+
+type SysRole struct {
+	RoleId       int       `orm:"column(role_id);pk" description:"角色ID"`
+	RoleName     string    `description:"角色名称"`
+	RoleType     string    `description:"角色类型"`
+	RoleTypeCode string    `description:"角色类型编码"`
+	CreateTime   time.Time `description:"创建时间"`
+	ModifyTime   time.Time
+}
+
+type SysRoleItem struct {
+	RoleId       int       `orm:"column(role_id);pk" description:"角色ID"`
+	RoleName     string    `description:"角色名称"`
+	RoleType     string    `description:"角色类型"`
+	RoleTypeCode string    `description:"角色类型编码"`
+	CreateTime   time.Time `description:"创建时间"`
+	RoleLevel    int       `description:"角色等级:0-表示一级角色,每个账号只能绑定一个一级角色 ,1-表示二级角色,每个账号可以绑定多个二级 角色"`
+}
+
+func GetSysRoleCount(roleName string) (count int, err error) {
+	sql := `SELECT COUNT(1) AS count FROM sys_role WHERE role_name=? `
+	o := orm.NewOrm()
+	err = o.Raw(sql, roleName).QueryRow(&count)
+	return
+}
+
+func AddSysRole(item *SysRole) (lastId int64, err error) {
+	o := orm.NewOrm()
+	lastId, err = o.Insert(item)
+	return
+}
+
+type SysRoleEditReq struct {
+	RoleId   int    `description:"角色ID"`
+	RoleName string `description:"角色名称"`
+	RoleType string `description:"角色类型"`
+}
+
+func GetSysRoleById(roleId int) (item *SysRoleItem, err error) {
+	sql := `SELECT * FROM sys_role WHERE role_id=? `
+	o := orm.NewOrm()
+	err = o.Raw(sql, roleId).QueryRow(&item)
+	return
+}
+
+func GetSysRoleByName(roleName string) (item *SysRoleItem, err error) {
+	sql := `SELECT * FROM sys_role WHERE role_name=? `
+	o := orm.NewOrm()
+	err = o.Raw(sql, roleName).QueryRow(&item)
+	return
+}
+
+// GetSysRoleByRoleTypeCode role_type_code
+func GetSysRoleByRoleTypeCode(roleTypeCode string) (item *SysRoleItem, err error) {
+	sql := `SELECT * FROM sys_role WHERE role_type_code=? `
+	o := orm.NewOrm()
+	err = o.Raw(sql, roleTypeCode).QueryRow(&item)
+	return
+}
+func ModifySysRole(roleName, roleType, roleTypeCode string, groupId int) (err error) {
+	sql := `UPDATE sys_role SET role_name=?,role_type=?,role_type_code=?,modify_time=NOW() WHERE role_id=? `
+	o := orm.NewOrm()
+	_, err = o.Raw(sql, roleName, roleType, roleTypeCode, groupId).Exec()
+	return
+}
+
+type SysRoleDeleteReq struct {
+	RoleId int `description:"角色ID"`
+}
+
+func DeleteSysRole(roleId int) (err error) {
+	sql := `DELETE FROM sys_role WHERE role_id=? `
+	o := orm.NewOrm()
+	_, err = o.Raw(sql, roleId).Exec()
+	return
+}
+
+func GetSysRoleListCount(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT COUNT(1) AS count FROM sys_role WHERE 1=1 `
+	if condition != "" {
+		sql += condition
+	}
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+func GetSysRoleList(condition string, pars []interface{}, startSize, pageSize int) (items []*SysRoleItem, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM sys_role WHERE 1=1 `
+	if condition != "" {
+		sql += condition
+	}
+	sql += `ORDER BY modify_time DESC LIMIT ?,?`
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+type SysRoleListResp struct {
+	List   []*SysRoleItem
+	Paging *paging.PagingItem `description:"分页数据"`
+}
+
+type RoleMenu struct {
+	MenuId int    `description:"导航唯一标识"`
+	Name   string `description:"导航名称"`
+	NameEn string `description:"导航名称(英文)"`
+}
+
+type RoleMenuList struct {
+	MenuId    int    `description:"导航唯一标识"`
+	Name      string `description:"导航名称"`
+	NameEn    string `description:"导航名称(英文)"`
+	Child     []*RoleMenu
+	CheckList []int
+}
+
+type RoleMenuListResp struct {
+	List []*RoleMenuList
+}
+
+func GetRoleMenuList() (items []*RoleMenuList, err error) {
+	sql := `SELECT * FROM sys_menu WHERE parent_id=0 ORDER BY sort ASC `
+	_, err = orm.NewOrm().Raw(sql).QueryRows(&items)
+	return
+}
+
+func GetRoleMenuByParentId(parentId int) (items []*RoleMenu, err error) {
+	sql := `SELECT * FROM sys_menu WHERE parent_id=? ORDER BY sort ASC `
+	_, err = orm.NewOrm().Raw(sql, parentId).QueryRows(&items)
+	return
+}
+
+func GetCheckListRoleMenu(roleId, parentId int) (items []*RoleMenuList, err error) {
+	sql := ` SELECT b.menu_id FROM sys_role_menu AS a
+				INNER JOIN sys_menu AS b ON a.menu_id=b.menu_id
+				WHERE a.role_id=? 
+				AND b.parent_id=? `
+	_, err = orm.NewOrm().Raw(sql, roleId, parentId).QueryRows(&items)
+	return
+}
+
+type RoleMenusAddReq struct {
+	RoleId          int    `description:"角色ID"`
+	MenuIdStr       string `description:"MenuId,多个用英文逗号隔开"`
+	MenuButtonIdStr string `description:"menu_button_id,多个用英文逗号隔开"`
+}
+
+func DeleteRoleMenuByRoleId(roleId int) (err error) {
+	sql := `DELETE FROM sys_role_menu WHERE role_id=? `
+	_, err = orm.NewOrm().Raw(sql, roleId).Exec()
+	return
+
+}
+
+type SysRoleMenu struct {
+	RoleMenuId int `orm:"column(role_menu_id);pk" description:"关联id"`
+	RoleId     int
+	MenuId     int
+}
+
+func AddSysRoleMenu(item *SysRoleMenu) (lastId int64, err error) {
+	o := orm.NewOrm()
+	lastId, err = o.Insert(item)
+	return
+}
+
+// CreateMultiSysRoleMenu 删除并新增角色权限
+func CreateMultiSysRoleMenu(roleId int, items []*SysRoleMenu) (err error) {
+	if roleId == 0 {
+		return
+	}
+	o := orm.NewOrm()
+	tx, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = tx.Rollback()
+			return
+		}
+		_ = tx.Commit()
+	}()
+
+	sql := `DELETE FROM sys_role_menu WHERE role_id = ?`
+	_, err = tx.Raw(sql, roleId).Exec()
+	if err != nil {
+		return
+	}
+
+	if len(items) > 0 {
+		_, err = tx.InsertMulti(len(items), items)
+	}
+	return
+}
+
+func GetSysRoleAll(levelFlag bool) (items []*SysRoleItem, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM sys_role WHERE 1=1`
+	if levelFlag {
+		sql += ` and role_level = 0`
+	}
+	sql += ` ORDER BY create_time DESC `
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}
+
+type SysRoleAllResp struct {
+	List []*SysRoleItem
+}
+
+// GetEnglishAuthRoleIds 获取英文权限角色IDs
+func GetEnglishAuthRoleIds(roleCodes []string) (roleIds []int, err error) {
+	if len(roleCodes) == 0 {
+		return
+	}
+	o := orm.NewOrm()
+	sql := `SELECT role_id FROM sys_role WHERE role_type_code IN (` + utils.GetOrmInReplace(len(roleCodes)) + `)`
+	_, err = o.Raw(sql, roleCodes).QueryRows(&roleIds)
+	return
+}
+
+// GetSysRoleMenuByRoleId 获取角色关联菜单
+func GetSysRoleMenuByRoleId(roleId int) (items []*SysRoleMenu, err error) {
+	sql := `SELECT
+				*
+			FROM
+				sys_role_menu
+			WHERE
+				role_id = ?`
+	_, err = orm.NewOrm().Raw(sql, roleId).QueryRows(&items)
+	return
+}
+
+// GetEnglishAuthRoleItems 获取英文权限角色
+func GetEnglishAuthRoleItems(roleCodes []string) (items []*SysRole, err error) {
+	if len(roleCodes) == 0 {
+		return
+	}
+	o := orm.NewOrm()
+	sql := `SELECT * FROM sys_role WHERE role_type_code IN (` + utils.GetOrmInReplace(len(roleCodes)) + `)`
+	_, err = o.Raw(sql, roleCodes).QueryRows(&items)
+	return
+}
+
+func GetSysRoleByIdList(id []int) (items []*SysRole, err error) {
+	if len(id) == 0 {
+		return
+	}
+	o := orm.NewOrm()
+	sql := `SELECT * FROM sys_role WHERE role_id IN (` + utils.GetOrmInReplace(len(id)) + `)`
+	_, err = o.Raw(sql, id).QueryRows(&items)
+	return
+}

+ 256 - 0
models/system/sys_role_admin.go

@@ -0,0 +1,256 @@
+package system
+
+import (
+	"eta/eta_forum_admin/utils"
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+type SysRoleAdmin struct {
+	Id         int       `orm:"column(id);pk" description:"自增ID"`
+	AdminId    int       `orm:"column(admin_id);" description:"系统用户id"`
+	RoleId     int       `orm:"column(role_id);" description:"角色ID"`
+	CreateTime time.Time `orm:"column(create_time);" description:"创建时间"`
+}
+
+type RoleAdminItem struct {
+	SysRoleAdminId  int    `description:"管理员账号和角色映射表id"`
+	AdminId         int    `description:"系统用户id"`
+	AdminName       string `description:"系统用户名称"`
+	RealName        string `description:"系统用户姓名"`
+	Enabled         int    `description:"1:有效,0:禁用"`
+	Mobile          string `description:"手机号"`
+	RoleId          int    `description:"角色Id"`
+	RoleTypeCode    string `description:"角色编码"`
+	DepartmentId    int    `description:"部门id"`
+	DepartmentName  string `description:"部门名称"`
+	DepartmentGroup string `description:"部门分组"`
+	TeamId          int    `description:"小组id"`
+	GroupId         int    `description:"分组id"`
+	GroupName       string `description:"分组名称"`
+	Authority       int    `description:"管理权限,0:无,1:部门负责人,2:小组负责人,或者ficc销售主管,4:ficc销售组长"`
+	LabelVal        int    `description:"标签:1:超级管理员,2:管理员,3:部门经理,4:组长,5:ficc销售主管"`
+}
+
+func (sa *SysRoleAdmin) TableName() string {
+	return "sys_role_admin"
+}
+
+// GetRoleIdsByAdminId 根据管理员账号查询角色ID
+func GetRoleIdsByAdminId(adminId int) (items []*SysRoleAdmin, err error) {
+	sql := `SELECT * from sys_role_admin where admin_id = ?`
+	_, err = orm.NewOrm().Raw(sql, adminId).QueryRows(&items)
+	return
+}
+
+// GetRoleIdsByAdminIdRoleId 根据管理员账号和角色查询
+func GetRoleIdsByAdminIdRoleId(adminId int, roleId int) (item *SysRoleAdmin, err error) {
+	sql := `SELECT * from sys_role_admin where admin_id = ? and role_id=?`
+	err = orm.NewOrm().Raw(sql, adminId, roleId).QueryRow(&item)
+	return
+}
+
+// GetRoleAdminList 查询对应角色绑定的管理员列表
+func GetRoleAdminList(condition string, pars []interface{}, startSize, pageSize int) (list []*RoleAdminItem, err error) {
+	sql := `SELECT
+	s.id as sys_role_admin_id,
+	a.admin_id,
+	a.admin_name,
+	a.real_name,
+	a.mobile,
+	a.department_id,
+	a.department_name,
+	a.enabled,
+	a.role_id,
+	a.role_type_code ,
+	a.group_id,
+	a.authority,
+	a.group_name
+FROM
+	sys_role_admin s
+	INNER JOIN admin a ON s.admin_id = a.admin_id 
+where 1=1`
+	if condition != "" {
+		sql += condition
+	}
+	sql += ` ORDER BY s.id DESC limit ?, ?`
+	_, err = orm.NewOrm().Raw(sql, pars, startSize, pageSize).QueryRows(&list)
+	return
+}
+
+// GetRoleAdminListTotal 查询对应角色绑定的管理员列表总数
+func GetRoleAdminListTotal(condition string, pars []interface{}) (total int64, err error) {
+	sql := `SELECT
+	count(*)
+FROM
+	sys_role_admin s
+	INNER JOIN admin a ON s.admin_id = a.admin_id 
+where 1=1`
+	if condition != "" {
+		sql += condition
+	}
+	err = orm.NewOrm().Raw(sql, pars).QueryRow(&total)
+	return
+}
+
+// CheckRoleAdminByAdminIds 判断是否已经绑定过该角色
+func CheckRoleAdminByAdminIds(adminIds string, roleId int) (list []*SysRoleAdmin, err error) {
+	sql := `SELECT
+	s.*
+FROM
+	sys_role_admin s
+	LEFT JOIN admin a ON s.admin_id = a.admin_id 
+where s.role_id=? and s.admin_id in (` + adminIds + `)`
+	_, err = orm.NewOrm().Raw(sql, roleId).QueryRows(&list)
+	return
+}
+
+// AddRoleAdminBatch 批量新增留言版管理员
+func AddRoleAdminBatch(admins []*SysRoleAdmin) (err error) {
+	o := orm.NewOrm()
+	_, err = o.InsertMulti(len(admins), admins)
+	return
+}
+
+// DeleteRoleAdmin 删除管理员和角色的绑定
+func DeleteRoleAdmin(SysRoleAdminId int) (err error) {
+	sql := `DELETE FROM sys_role_admin WHERE id=? `
+	o := orm.NewOrm()
+	_, err = o.Raw(sql, SysRoleAdminId).Exec()
+	return
+}
+
+// GetRoleAdminById 根据ID查询绑定记录
+func GetRoleAdminById(SysRoleAdminId int) (item *SysRoleAdmin, err error) {
+	sql := `SELECT
+	*
+FROM
+	sys_role_admin 
+where id=?`
+	err = orm.NewOrm().Raw(sql, SysRoleAdminId).QueryRow(&item)
+	return
+}
+
+// GetEnglishAuthRoleAdminList 查询英文权限管理员列表
+func GetEnglishAuthRoleAdminList(condition string, pars []interface{}, startSize, pageSize int) (total int, list []*RoleAdminItem, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT
+				s.id as sys_role_admin_id,
+				a.admin_id,
+				a.admin_name,
+				a.real_name,
+				a.mobile,
+				a.department_id,
+				a.department_name,
+				a.enabled,
+				a.role_id,
+				a.role_type_code ,
+				a.group_id,
+				a.authority,
+				a.group_name
+			FROM
+				sys_role_admin AS s
+			JOIN sys_role AS r ON s.role_id = r.role_id
+			JOIN admin AS a ON s.admin_id = a.admin_id
+			WHERE
+				1 = 1 `
+	sql += condition
+	sql += ` GROUP BY s.admin_id `
+
+	totalSQl := `SELECT COUNT(1) total FROM (` + sql + `) z`
+	if err = o.Raw(totalSQl, pars).QueryRow(&total); err != nil {
+		return
+	}
+
+	sql += ` ORDER BY s.create_time DESC,s.admin_id ASC LIMIT ?,? `
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&list)
+	return
+}
+
+// DeleteRoleAdminByAdminIdAndRoleIds 通过管理员ID及角色IDs删除绑定
+func DeleteRoleAdminByAdminIdAndRoleIds(adminId int, roleIds []int) (err error) {
+	if len(roleIds) == 0 {
+		return
+	}
+	o := orm.NewOrm()
+	sql := `DELETE FROM sys_role_admin WHERE admin_id = ? AND role_id IN (` + utils.GetOrmInReplace(len(roleIds)) + `) `
+	_, err = o.Raw(sql, adminId, roleIds).Exec()
+	return
+}
+
+// CheckEnglishAuthRoleAdminByAdminIds 通过管理员IDs判断是否已经绑定过英文权限角色
+func CheckEnglishAuthRoleAdminByAdminIds(adminIds []int, roleCodes []string) (list []*SysRoleAdmin, err error) {
+	if len(adminIds) == 0 || len(roleCodes) == 0 {
+		return
+	}
+	o := orm.NewOrm()
+	sql := `SELECT
+				s.*
+			FROM
+				sys_role_admin AS s
+			JOIN sys_role AS r ON s.role_id = r.role_id
+			JOIN admin AS a ON s.admin_id = a.admin_id
+			WHERE
+				s.admin_id IN (` + utils.GetOrmInReplace(len(adminIds)) + `) AND r.role_type_code IN (` + utils.GetOrmInReplace(len(roleCodes)) + `)
+			GROUP BY
+				s.admin_id`
+	_, err = o.Raw(sql, adminIds, roleCodes).QueryRows(&list)
+	return
+}
+
+// EnglishAuthRoleDetail 英文权限角色详情
+type EnglishAuthRoleDetail struct {
+	AdminId      int    `description:"用户ID"`
+	RoleTypeCode string `description:"角色类型编码"`
+}
+
+// GetAdminEnglishAuthRoleDetail 获取用户英文权限详情
+func GetAdminEnglishAuthRoleDetail(adminId int, roleCodes []string) (list []*EnglishAuthRoleDetail, err error) {
+	if len(roleCodes) == 0 {
+		return
+	}
+	o := orm.NewOrm()
+	sql := `SELECT
+				s.admin_id, r.role_type_code
+			FROM
+				sys_role_admin AS s
+			JOIN sys_role AS r ON s.role_id = r.role_id
+			JOIN admin AS a ON s.admin_id = a.admin_id
+			WHERE
+				s.admin_id = ? AND r.role_type_code IN (` + utils.GetOrmInReplace(len(roleCodes)) + `)`
+	_, err = o.Raw(sql, adminId, roleCodes).QueryRows(&list)
+	return
+}
+
+// EditEnglishAuthRoleAdmin 编辑管理员英文权限
+func EditEnglishAuthRoleAdmin(adminId int, englishRoleIds []int, roleAdmins []*SysRoleAdmin) (err error) {
+	o := orm.NewOrm()
+	tx, e := o.Begin()
+	if e != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = tx.Rollback()
+		} else {
+			_ = tx.Commit()
+		}
+	}()
+
+	// 删除原绑定关系
+	sql := `DELETE FROM sys_role_admin WHERE admin_id = ? AND role_id IN (` + utils.GetOrmInReplace(len(englishRoleIds)) + `)`
+	_, err = tx.Raw(sql, adminId, englishRoleIds).Exec()
+	if err != nil {
+		return
+	}
+
+	// 新增绑定关系
+	_, err = tx.InsertMulti(len(roleAdmins), roleAdmins)
+	return
+}
+
+// SyncAdminEnRole 同步用户英文角色
+type SyncAdminEnRole struct {
+	AdminName string   `description:"用户名"`
+	RoleCodes []string `description:"角色Codes"`
+}

+ 84 - 0
models/system/sys_team.go

@@ -0,0 +1,84 @@
+package system
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+type SysTeamAddReq struct {
+	GroupId      int    `description:"大组id"`
+	DepartmentId int    `description:"部门id"`
+	TeamName     string `description:"分组名称,多个用英文逗号隔开"`
+}
+
+type SysTeam struct {
+	GroupId      int       `orm:"column(group_id);pk" description:"分组ID"`
+	ParentId     int       `description:"父级Id"`
+	DepartmentId int       `description:"部门id"`
+	GroupName    string    `description:"分组名称"`
+	CreateTime   time.Time `description:"创建时间"`
+}
+
+func GetSysTeamCount(groupId int, teamName string) (count int, err error) {
+	sql := `SELECT COUNT(1) AS count FROM sys_group WHERE parent_id=? AND group_name=? `
+	o := orm.NewOrm()
+	err = o.Raw(sql, groupId, teamName).QueryRow(&count)
+	return
+}
+
+type SysTeamEditReq struct {
+	TeamId   int    `description:"分组ID"`
+	TeamName string `description:"分组名称"`
+}
+
+func GetSysTeamByName(teamName string) (item *SysGroup, err error) {
+	sql := `SELECT * FROM sys_group WHERE group_name=? `
+	o := orm.NewOrm()
+	err = o.Raw(sql, teamName).QueryRow(&item)
+	return
+}
+
+func ModifySysTeam(teamName string, teamId int) (err error) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	sql := `UPDATE sys_group SET group_name=? WHERE group_id=? `
+	_, err = to.Raw(sql, teamName, teamId).Exec()
+	sql = `UPDATE admin SET group_name=? WHERE group_id=? `
+	_, err = to.Raw(sql, teamName, teamId).Exec()
+	return
+}
+
+type SysTeamDeleteReq struct {
+	TeamId int `description:"小组ID"`
+}
+
+type SysTeamList struct {
+	GroupId    int       `orm:"column(group_id);pk" json:"DepartmentId" description:"小组ID"`
+	ParentId   int       `json:"GroupId" description:"小组Id"`
+	GroupName  string    `json:"DepartmentName" description:"分组名称"`
+	CreateTime time.Time `description:"创建时间"`
+}
+
+func GetSysTeamByDepartmentId(groupId int) (items []*SysTeamList, err error) {
+	sql := `SELECT * FROM sys_group WHERE parent_id=? ORDER BY sort ASC, create_time ASC`
+	o := orm.NewOrm()
+	_, err = o.Raw(sql, groupId).QueryRows(&items)
+	return
+}
+
+func ClearSysUserTeam(groupId int) (err error) {
+	sql := `UPDATE admin SET group_id=0,group_name='' WHERE group_id=? `
+	o := orm.NewOrm()
+	_, err = o.Raw(sql, groupId).Exec()
+	return
+}

+ 218 - 0
models/user.go

@@ -0,0 +1,218 @@
+package models
+
+import (
+	"eta/eta_forum_admin/utils"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"time"
+)
+
+type User struct {
+	UserId               int `orm:"column(user_id);pk"`
+	BusinessCode         string
+	EtaBusinessId        int
+	UserName             string `description:"用户名"`
+	RealName             string `description:"姓名"`
+	Mobile               string
+	Email                string
+	NickName             string `description:"昵称"`
+	CountryCode          string `description:"区号,86、852、886等"`
+	LastLoginTime        time.Time
+	IsFreeLogin          int `description:"是否30天免登录,0否,1是"`
+	RegisterTime         time.Time
+	LastCollectChartTime string `description:"最近收藏图表时间"`
+	Enabled              int
+	DepartmentName       string `description:"部门名称"`
+	Position             string `description:"岗位"`
+	PositionStatus       int    `description:"在职状态:1:在职,0:离职"`
+	CreatedTime          time.Time
+	LastUpdatedTime      time.Time `description:"最近一次更新时间"`
+	SellerId             int       `description:"销售ID"`
+	SellerName           string    `description:"销售名称"`
+}
+
+// 用户详情出参
+type UserResp struct {
+	Detail *User
+}
+
+func AddUser(item *User) (lastId int64, err error) {
+	o := orm.NewOrm()
+	lastId, err = o.Insert(item)
+	return
+}
+
+func GetUserByMobile(mobile string) (item *User, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM user WHERE mobile = ? LIMIT 1`
+	err = o.Raw(sql, mobile).QueryRow(&item)
+	return
+}
+
+func GetUserByMobiles(mobiles []string) (items []*User, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM user WHERE mobile in (` + utils.GetOrmInReplace(len(mobiles)) + `)`
+	_, err = o.Raw(sql, mobiles).QueryRows(&items)
+	return
+}
+
+// GetUserByMobileCountryCode 根据手机号和区号获取用户信息
+func GetUserByMobileCountryCode(mobile, countryCode string) (item *User, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM user WHERE mobile = ? `
+	sql += ` and country_code =? `
+	sql += ` LIMIT 1 `
+	err = o.Raw(sql, mobile, countryCode).QueryRow(&item)
+	return
+}
+
+func GetUserByUserId(userId int) (item *User, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM user WHERE user_id=? `
+	err = o.Raw(sql, userId).QueryRow(&item)
+	return
+}
+
+// 更新User信息
+func (User *User) Update(cols []string) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Update(User, cols...)
+	return
+}
+
+// 获取该用户数量
+func GetUserCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrm()
+	tmpSql := `SELECT *
+			FROM
+				user
+			WHERE
+				1=1 `
+	if condition != "" {
+		tmpSql += condition
+	}
+	sql := `SELECT COUNT(1) AS count FROM (` + tmpSql + `) AS c `
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+// 获取该用户列表
+func GetUserPageListByCondition(condition string, pars []interface{}, order string, startSize, pageSize int) (items []*User, err error) {
+	o := orm.NewOrm()
+	tmpSql := `SELECT *
+			FROM
+				user
+			WHERE
+				1=1 `
+	if condition != "" {
+		tmpSql += condition
+	}
+	if order != "" {
+		tmpSql += ` ORDER BY ` + order + ", user_id DESC"
+	} else {
+		tmpSql += ` ORDER BY  last_updated_time desc, user_id DESC`
+	}
+	tmpSql += ` Limit ?,?`
+	_, err = o.Raw(tmpSql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+type BusinessUser struct {
+	UserId             int
+	BusinessCode       string
+	EtaBusinessId      int
+	BusinessName       string
+	UserName           string `description:"用户名"`
+	RealName           string `description:"姓名"`
+	Mobile             string
+	Email              string
+	CountryCode        string `description:"区号,86、852、886等"`
+	LastLoginTime      string
+	Enabled            int
+	DepartmentName     string `description:"部门名称"`
+	Position           string `description:"岗位"`
+	PositionStatus     int    `description:"在职状态:1:在职,0:离职"`
+	CreatedTime        string
+	LastUpdatedTime    string `description:"最近一次更新时间"`
+	BusinessSellerId   int    `description:"销售ID"`
+	BusinessSellerName string `description:"销售名称"`
+}
+
+type UserListResp struct {
+	Paging *paging.PagingItem
+	List   []*BusinessUser
+}
+
+// 新增用户请求参数
+type AddUserReq struct {
+	RealName    string `description:"姓名"`
+	CountryCode string `description:"区号,86、852、886等"`
+	Mobile      string `description:"手机号"`
+	//Email          string `description:"邮箱"`
+	Position       string `description:"职位"`
+	PositionStatus int    `description:"在职状态:1:在职,0:离职"`
+	EtaBusinessId  int
+	DepartmentName string `description:"联系人部门"`
+}
+
+// 删除客户请求参数
+type DeleteUserReq struct {
+	UserId int
+}
+
+// 新增客户请求参数
+type EditUserReq struct {
+	UserId      int
+	RealName    string `description:"姓名"`
+	CountryCode string `description:"区号,86、852、886等"`
+	Mobile      string `description:"手机号"`
+	//Email          string `description:"邮箱"`
+	Position       string `description:"职位"`
+	PositionStatus int    `description:"在职状态:1:在职,0:离职"`
+	EtaBusinessId  int
+	DepartmentName string `description:"联系人部门"`
+}
+
+func DeleteUser(userId int) (err error) {
+	o := orm.NewOrm()
+	sql := ` DELETE FROM user WHERE user_id=? `
+	_, err = o.Raw(sql, userId).Exec()
+	return
+}
+
+// 联系人导入预览数据返回
+type ImportListResp struct {
+	ValidUser  []*BusinessUser `description:"有效客户数据"`
+	RepeatUser []*BusinessUser `description:"重复客户数据"`
+}
+
+// 联系人导入预览数据返回
+type ImportUserReq struct {
+	ValidUser []*ImportUserItem `description:"有效客户数据"`
+}
+
+type ImportUserItem struct {
+	BusinessCode   string
+	EtaBusinessId  int
+	UserName       string `description:"用户名"`
+	RealName       string `description:"姓名"`
+	Mobile         string
+	NickName       string `description:"昵称"`
+	CountryCode    string `description:"区号,86、852、886等"`
+	DepartmentName string `description:"部门名称"`
+	Position       string `description:"岗位"`
+	PositionStatus int    `description:"在职状态:1:在职,0:离职"`
+}
+
+// 新增客户请求参数
+type ChangeUserBusinessReq struct {
+	UserId        int
+	EtaBusinessId int
+	//BusinessCode  string `description:"商家编码"`
+}
+
+// UserEditEnabledReq 用户状态编辑
+type UserEditEnabledReq struct {
+	UserId  int `description:"系统用户id"`
+	Enabled int `description:"1:有效,0:禁用"`
+}

+ 891 - 0
routers/commentsRouter.go

@@ -7,6 +7,528 @@ import (
 
 func init() {
 
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessController"],
+        beego.ControllerComments{
+            Method: "Add",
+            Router: `/add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessController"],
+        beego.ControllerComments{
+            Method: "AddChartPermission",
+            Router: `/add_chart_permission`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessController"],
+        beego.ControllerComments{
+            Method: "ContractList",
+            Router: `/contract_list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessController"],
+        beego.ControllerComments{
+            Method: "Detail",
+            Router: `/detail`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessController"],
+        beego.ControllerComments{
+            Method: "Edit",
+            Router: `/edit`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessController"],
+        beego.ControllerComments{
+            Method: "EditSign",
+            Router: `/edit_sign`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessController"],
+        beego.ControllerComments{
+            Method: "Enable",
+            Router: `/enable`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessController"],
+        beego.ControllerComments{
+            Method: "GetChartPermission",
+            Router: `/get_chart_permission`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessController"],
+        beego.ControllerComments{
+            Method: "AllList",
+            Router: `/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessController"],
+        beego.ControllerComments{
+            Method: "MoveSeller",
+            Router: `/move_seller`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessController"],
+        beego.ControllerComments{
+            Method: "OperateRecordList",
+            Router: `/operate_record_list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessController"],
+        beego.ControllerComments{
+            Method: "PageList",
+            Router: `/page_list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessController"],
+        beego.ControllerComments{
+            Method: "RemoveSign",
+            Router: `/remove_sign`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessController"],
+        beego.ControllerComments{
+            Method: "Signing",
+            Router: `/signing`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessMenuController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessMenuController"],
+        beego.ControllerComments{
+            Method: "SystemApiConfig",
+            Router: `/api/config/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessMenuController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessMenuController"],
+        beego.ControllerComments{
+            Method: "SystemApiConfigSet",
+            Router: `/api/config/set`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessMenuController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessMenuController"],
+        beego.ControllerComments{
+            Method: "Add",
+            Router: `/menu/add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessMenuController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessMenuController"],
+        beego.ControllerComments{
+            Method: "Edit",
+            Router: `/menu/edit`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessMenuController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessMenuController"],
+        beego.ControllerComments{
+            Method: "IconAdd",
+            Router: `/menu/icon/add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessMenuController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessMenuController"],
+        beego.ControllerComments{
+            Method: "IconList",
+            Router: `/menu/icon/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessMenuController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessMenuController"],
+        beego.ControllerComments{
+            Method: "List",
+            Router: `/menu/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessMenuController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessMenuController"],
+        beego.ControllerComments{
+            Method: "SaveRelate",
+            Router: `/menu/relate/save`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessMenuController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessMenuController"],
+        beego.ControllerComments{
+            Method: "Remove",
+            Router: `/menu/remove`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessUserController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessUserController"],
+        beego.ControllerComments{
+            Method: "AreaCodeList",
+            Router: `/area_code/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessUserController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessUserController"],
+        beego.ControllerComments{
+            Method: "AddUser",
+            Router: `/user/add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessUserController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessUserController"],
+        beego.ControllerComments{
+            Method: "ChangeUserBusiness",
+            Router: `/user/changeBusiness`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessUserController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessUserController"],
+        beego.ControllerComments{
+            Method: "DeleteUser",
+            Router: `/user/delete`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessUserController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessUserController"],
+        beego.ControllerComments{
+            Method: "EditUser",
+            Router: `/user/edit`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessUserController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessUserController"],
+        beego.ControllerComments{
+            Method: "EditEnabled",
+            Router: `/user/editEnabled`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessUserController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessUserController"],
+        beego.ControllerComments{
+            Method: "Import",
+            Router: `/user/import`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessUserController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessUserController"],
+        beego.ControllerComments{
+            Method: "ImportList",
+            Router: `/user/import/list`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessUserController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessUserController"],
+        beego.ControllerComments{
+            Method: "List",
+            Router: `/user/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessUserController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_business:EtaBusinessUserController"],
+        beego.ControllerComments{
+            Method: "TemplateDownload",
+            Router: `/user/template`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_training_video:EtaTrainingVideoClassifyController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_training_video:EtaTrainingVideoClassifyController"],
+        beego.ControllerComments{
+            Method: "Add",
+            Router: `/classify/add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_training_video:EtaTrainingVideoClassifyController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_training_video:EtaTrainingVideoClassifyController"],
+        beego.ControllerComments{
+            Method: "Edit",
+            Router: `/classify/edit`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_training_video:EtaTrainingVideoClassifyController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_training_video:EtaTrainingVideoClassifyController"],
+        beego.ControllerComments{
+            Method: "Remove",
+            Router: `/classify/remove`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_training_video:EtaTrainingVideoClassifyController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_training_video:EtaTrainingVideoClassifyController"],
+        beego.ControllerComments{
+            Method: "Tree",
+            Router: `/classify/tree`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_training_video:EtaTrainingVideoController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_training_video:EtaTrainingVideoController"],
+        beego.ControllerComments{
+            Method: "Add",
+            Router: `/add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_training_video:EtaTrainingVideoController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_training_video:EtaTrainingVideoController"],
+        beego.ControllerComments{
+            Method: "Detail",
+            Router: `/detail`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_training_video:EtaTrainingVideoController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_training_video:EtaTrainingVideoController"],
+        beego.ControllerComments{
+            Method: "Edit",
+            Router: `/edit`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_training_video:EtaTrainingVideoController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_training_video:EtaTrainingVideoController"],
+        beego.ControllerComments{
+            Method: "PageList",
+            Router: `/page_list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_training_video:EtaTrainingVideoController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_training_video:EtaTrainingVideoController"],
+        beego.ControllerComments{
+            Method: "Publish",
+            Router: `/publish`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_training_video:EtaTrainingVideoController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_training_video:EtaTrainingVideoController"],
+        beego.ControllerComments{
+            Method: "Remove",
+            Router: `/remove`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_training_video:EtaTrainingVideoTagController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_training_video:EtaTrainingVideoTagController"],
+        beego.ControllerComments{
+            Method: "Add",
+            Router: `/tag/add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_training_video:EtaTrainingVideoTagController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_training_video:EtaTrainingVideoTagController"],
+        beego.ControllerComments{
+            Method: "Edit",
+            Router: `/tag/edit`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_training_video:EtaTrainingVideoTagController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_training_video:EtaTrainingVideoTagController"],
+        beego.ControllerComments{
+            Method: "PageList",
+            Router: `/tag/page_list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_training_video:EtaTrainingVideoTagController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/eta_training_video:EtaTrainingVideoTagController"],
+        beego.ControllerComments{
+            Method: "Remove",
+            Router: `/tag/remove`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/help_doc:HelpDocClassifyController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/help_doc:HelpDocClassifyController"],
+        beego.ControllerComments{
+            Method: "AddClassify",
+            Router: `/classify/add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/help_doc:HelpDocClassifyController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/help_doc:HelpDocClassifyController"],
+        beego.ControllerComments{
+            Method: "DelClassify",
+            Router: `/classify/delete`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/help_doc:HelpDocClassifyController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/help_doc:HelpDocClassifyController"],
+        beego.ControllerComments{
+            Method: "EditClassify",
+            Router: `/classify/edit`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/help_doc:HelpDocClassifyController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/help_doc:HelpDocClassifyController"],
+        beego.ControllerComments{
+            Method: "ListClassify",
+            Router: `/classify/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/help_doc:HelpDocClassifyController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/help_doc:HelpDocClassifyController"],
+        beego.ControllerComments{
+            Method: "ClassifyMove",
+            Router: `/classify/move`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/help_doc:HelpDocClassifyController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/help_doc:HelpDocClassifyController"],
+        beego.ControllerComments{
+            Method: "EditClassifyVisible",
+            Router: `/classify/visible/edit`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/help_doc:HelpDocController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/help_doc:HelpDocController"],
+        beego.ControllerComments{
+            Method: "Add",
+            Router: `/add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/help_doc:HelpDocController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/help_doc:HelpDocController"],
+        beego.ControllerComments{
+            Method: "Delete",
+            Router: `/delete`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/help_doc:HelpDocController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/help_doc:HelpDocController"],
+        beego.ControllerComments{
+            Method: "Detail",
+            Router: `/detail`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/help_doc:HelpDocController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/help_doc:HelpDocController"],
+        beego.ControllerComments{
+            Method: "ListReport",
+            Router: `/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/help_doc:HelpDocController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers/help_doc:HelpDocController"],
+        beego.ControllerComments{
+            Method: "PublishReport",
+            Router: `/publish`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:AdminController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:AdminController"],
         beego.ControllerComments{
             Method: "GetBusinessAdmin",
@@ -88,6 +610,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ChartClassifyController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ChartClassifyController"],
+        beego.ControllerComments{
+            Method: "ClassifyRealTree",
+            Router: `/classify/real_tree`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ChartClassifyController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ChartClassifyController"],
         beego.ControllerComments{
             Method: "SimpleList",
@@ -115,6 +646,33 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ChartCollectStatController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ChartCollectStatController"],
+        beego.ControllerComments{
+            Method: "ChartStat",
+            Router: `/stat/chart`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ChartCollectStatController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ChartCollectStatController"],
+        beego.ControllerComments{
+            Method: "UserStat",
+            Router: `/stat/user`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ChartCollectStatController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ChartCollectStatController"],
+        beego.ControllerComments{
+            Method: "UserDetailList",
+            Router: `/stat/user/detailList`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ChartInfoController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ChartInfoController"],
         beego.ControllerComments{
             Method: "ChartInfoDetail",
@@ -142,6 +700,231 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:CompanyIndustryController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:CompanyIndustryController"],
+        beego.ControllerComments{
+            Method: "List",
+            Router: `/industry/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:CompanySellerController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:CompanySellerController"],
+        beego.ControllerComments{
+            Method: "CompanyQCCSearch",
+            Router: `/potential/company/qccSearch`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:CompanySellerController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:CompanySellerController"],
+        beego.ControllerComments{
+            Method: "CheckListV2",
+            Router: `/seller/check/listV2`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:CompanySellerController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:CompanySellerController"],
+        beego.ControllerComments{
+            Method: "RoadshowFiccList",
+            Router: `/seller/roadshow_ficc/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:CrmConfigController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:CrmConfigController"],
+        beego.ControllerComments{
+            Method: "Fetch",
+            Router: `/fetch`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:CrmConfigController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:CrmConfigController"],
+        beego.ControllerComments{
+            Method: "Save",
+            Router: `/save`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ETATrialController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ETATrialController"],
+        beego.ControllerComments{
+            Method: "AccountTransfer",
+            Router: `/account/transfer`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ETATrialController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ETATrialController"],
+        beego.ControllerComments{
+            Method: "Add",
+            Router: `/add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ETATrialController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ETATrialController"],
+        beego.ControllerComments{
+            Method: "AccountList",
+            Router: `/apply/accountlist`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ETATrialController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ETATrialController"],
+        beego.ControllerComments{
+            Method: "Approval",
+            Router: `/apply/approval`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ETATrialController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ETATrialController"],
+        beego.ControllerComments{
+            Method: "ApplyDel",
+            Router: `/apply/del`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ETATrialController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ETATrialController"],
+        beego.ControllerComments{
+            Method: "ApplyEnable",
+            Router: `/apply/enable`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ETATrialController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ETATrialController"],
+        beego.ControllerComments{
+            Method: "ApplyList",
+            Router: `/apply/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ETATrialController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ETATrialController"],
+        beego.ControllerComments{
+            Method: "MyList",
+            Router: `/apply/myList`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ETATrialController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ETATrialController"],
+        beego.ControllerComments{
+            Method: "ApplyReject",
+            Router: `/apply/reject`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ETATrialController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ETATrialController"],
+        beego.ControllerComments{
+            Method: "ApplyRevoke",
+            Router: `/apply/revoke`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ETATrialController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ETATrialController"],
+        beego.ControllerComments{
+            Method: "EnableAccountList",
+            Router: `/enable/account/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ETATrialController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ETATrialController"],
+        beego.ControllerComments{
+            Method: "List",
+            Router: `/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ETATrialController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ETATrialController"],
+        beego.ControllerComments{
+            Method: "QuestionnaireAnswerList",
+            Router: `/questionnaire/answerList`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ETATrialController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ETATrialController"],
+        beego.ControllerComments{
+            Method: "QuestionnairesDel",
+            Router: `/questionnaire/del`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ETATrialController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ETATrialController"],
+        beego.ControllerComments{
+            Method: "QuestionnaireDetail",
+            Router: `/questionnaire/detail`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ETATrialController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ETATrialController"],
+        beego.ControllerComments{
+            Method: "QuestionnaireDetailList",
+            Router: `/questionnaire/detailList`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ETATrialController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ETATrialController"],
+        beego.ControllerComments{
+            Method: "QuestionnaireList",
+            Router: `/questionnaire/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ETATrialController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ETATrialController"],
+        beego.ControllerComments{
+            Method: "QuestionnairesSave",
+            Router: `/questionnaire/save`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ETATrialController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ETATrialController"],
+        beego.ControllerComments{
+            Method: "QuestionnairesStatistical",
+            Router: `/questionnaire/statistical`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:EdbInfoController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:EdbInfoController"],
         beego.ControllerComments{
             Method: "TraceEdbInfo",
@@ -151,6 +934,42 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:EtaVersionUpdateLogController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:EtaVersionUpdateLogController"],
+        beego.ControllerComments{
+            Method: "Add",
+            Router: `/add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:EtaVersionUpdateLogController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:EtaVersionUpdateLogController"],
+        beego.ControllerComments{
+            Method: "Del",
+            Router: `/del`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:EtaVersionUpdateLogController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:EtaVersionUpdateLogController"],
+        beego.ControllerComments{
+            Method: "Edit",
+            Router: `/edit`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:EtaVersionUpdateLogController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:EtaVersionUpdateLogController"],
+        beego.ControllerComments{
+            Method: "PageList",
+            Router: `/page_list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:LoginController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:LoginController"],
         beego.ControllerComments{
             Method: "AuthCodeLogin",
@@ -160,4 +979,76 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ResourceController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ResourceController"],
+        beego.ControllerComments{
+            Method: "ImageUpload",
+            Router: `/image/upload`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ResourceController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ResourceController"],
+        beego.ControllerComments{
+            Method: "OssSTSToken",
+            Router: `/oss/get_sts_token`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:SysRoleController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:SysRoleController"],
+        beego.ControllerComments{
+            Method: "SysMenuButtons",
+            Router: `/menu/buttons`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:SysRoleController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:SysRoleController"],
+        beego.ControllerComments{
+            Method: "SysMenuList",
+            Router: `/menu/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:SysRoleController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:SysRoleController"],
+        beego.ControllerComments{
+            Method: "AllSysRole",
+            Router: `/role/all`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:SysRoleController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:SysRoleController"],
+        beego.ControllerComments{
+            Method: "ListSysRole",
+            Router: `/role/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:SysRoleController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:SysRoleController"],
+        beego.ControllerComments{
+            Method: "SysRoleMenusAdd",
+            Router: `/role/menu/add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:SysRoleController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:SysRoleController"],
+        beego.ControllerComments{
+            Method: "SysRoleMenusList",
+            Router: `/role/menu/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
 }

+ 61 - 0
routers/router.go

@@ -9,6 +9,9 @@ package routers
 
 import (
 	"eta/eta_forum_admin/controllers"
+	"eta/eta_forum_admin/controllers/eta_business"
+	"eta/eta_forum_admin/controllers/eta_training_video"
+	"eta/eta_forum_admin/controllers/help_doc"
 	"github.com/beego/beego/v2/server/web"
 )
 
@@ -35,6 +38,64 @@ func init() {
 				&controllers.AdminController{},
 			),
 		),
+		web.NSNamespace("/system",
+			web.NSInclude(
+				&controllers.SysRoleController{},
+				&controllers.SysMenuController{},
+			),
+		),
+		web.NSNamespace("/eta_business",
+			web.NSInclude(
+				&eta_business.EtaBusinessController{},
+				&eta_business.EtaBusinessMenuController{},
+				&eta_business.EtaBusinessUserController{},
+			),
+		),
+		web.NSNamespace("/eta_trial",
+			web.NSInclude(
+				&controllers.ETATrialController{},
+			),
+		),
+		web.NSNamespace("/eta_version_update_log",
+			web.NSInclude(
+				&controllers.EtaVersionUpdateLogController{},
+			),
+		),
+		web.NSNamespace("/help_doc",
+			web.NSInclude(
+				&help_doc.HelpDocClassifyController{},
+				&help_doc.HelpDocController{},
+			),
+		),
+		web.NSNamespace("/chart_collect",
+			web.NSInclude(
+				&controllers.ChartCollectStatController{},
+			),
+		),
+		web.NSNamespace("/resource",
+			web.NSInclude(
+				&controllers.ResourceController{},
+			),
+		),
+		web.NSNamespace("/config",
+			web.NSInclude(
+				&controllers.CrmConfigController{},
+			),
+		),
+
+		web.NSNamespace("/eta_training_video",
+			web.NSInclude(
+				&eta_training_video.EtaTrainingVideoController{},
+				&eta_training_video.EtaTrainingVideoClassifyController{},
+				&eta_training_video.EtaTrainingVideoTagController{},
+			),
+		),
+		web.NSNamespace("/custom",
+			web.NSInclude(
+				&controllers.CompanyIndustryController{},
+				&controllers.CompanySellerController{},
+			),
+		),
 	)
 	web.AddNamespace(ns)
 }

+ 162 - 0
services/aws_s3.go

@@ -0,0 +1,162 @@
+package services
+
+import (
+	"bytes"
+	"crypto/tls"
+	"eta/eta_forum_admin/utils"
+	"fmt"
+	"github.com/aws/aws-sdk-go/aws"
+	"github.com/aws/aws-sdk-go/aws/credentials"
+	"github.com/aws/aws-sdk-go/aws/session"
+	"github.com/aws/aws-sdk-go/service/s3"
+	"io/ioutil"
+	"net/http"
+	"time"
+)
+
+type OssClient interface {
+	UploadFile(string, string, string) (string, error)
+	GetUploadToken() (OssToken, error)
+}
+
+func NewOssClient() OssClient {
+	switch utils.ObjectStorageClient {
+	case utils.STORAGESOURCE_MINIO_NAME:
+		return new(MinioOss)
+	case utils.STORAGESOURCE_S3_NAME:
+		return new(S3Oss)
+	default:
+		// 默认使用阿里云OSS
+		return new(AliOss)
+	}
+}
+
+// OssToken 此处为了兼容前端那边所以有重复的
+type OssToken struct {
+	AccessKeyId string
+	SecretKeyId string
+	RegionId    string
+	Bucketname  string
+	Endpoint    string
+	ImgHost     string
+	UseSSL      string
+	Port        string
+	//AccessKeyId     string
+	AccessKeySecret string
+	SecurityToken   string
+	ExpiredTime     string
+	//RegionId        string
+	//Bucketname      string
+	//Endpoint        string
+	Imghost      string
+	S3ForceStyle bool
+	S3Protocol   string
+}
+
+type S3Oss struct{}
+
+func (m *S3Oss) UploadFile(fileName, localFile, savePath string) (resourceUrl string, err error) {
+	defer func() {
+		if err != nil {
+			fmt.Println(err.Error())
+		}
+	}()
+
+	// 默认使用后端这个, 这里有两个配置的原因是
+	// 前端上传跨域问题可能会使用反向代理来解决, 这样的话同一个endpoint就会导致一端正常另一端不正常
+	endpoint := utils.S3BackEndpoint
+	if endpoint == "" {
+		endpoint = utils.S3Endpoint
+	}
+	accessKey := utils.S3AccessKeyId
+	secretKey := utils.S3AccessKeySecret
+	region := utils.S3Region
+	bucketName := utils.S3BucketName
+	uploadDir := utils.S3UploadDir
+	resourceHost := utils.S3Host
+	forceStyle := utils.S3ForceStyle
+	hostStyle := true // 默认true, 使用`endpoint/bucket_name`这种HOST格式
+	if forceStyle == "false" {
+		hostStyle = false
+	}
+	disableSSL := true // 默认true, 跳过SSL
+	if utils.S3DisableSSL == "false" {
+		disableSSL = false
+	}
+	//fmt.Println("disableSSL: ", disableSSL)
+
+	config := &aws.Config{
+		Region:           aws.String(region),
+		Credentials:      credentials.NewStaticCredentials(accessKey, secretKey, ""),
+		Endpoint:         aws.String(endpoint),
+		S3ForcePathStyle: aws.Bool(hostStyle),
+		DisableSSL:       aws.Bool(disableSSL),
+	}
+	if disableSSL {
+		config.HTTPClient = &http.Client{
+			Transport: &http.Transport{
+				TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
+			},
+		}
+	}
+	//b, _ := json.Marshal(config)
+	//fmt.Println(string(b))
+
+	// 创建AWS会话
+	sess, e := session.NewSession(config)
+	if e != nil {
+		err = fmt.Errorf("new session err: %s", e.Error())
+		return
+	}
+
+	// 创建S3服务客户端
+	client := s3.New(sess)
+
+	// 读取文件内容
+	fileContent, e := ioutil.ReadFile(localFile)
+	if e != nil {
+		err = fmt.Errorf("read file err: %s", e.Error())
+		return
+	}
+
+	path := savePath
+	if savePath == "" {
+		path = uploadDir + time.Now().Format("200601/20060102/") + fileName
+	}
+	putObjectInput := &s3.PutObjectInput{
+		Bucket: aws.String(bucketName),
+		Key:    aws.String(path),
+		Body:   bytes.NewReader(fileContent),
+	}
+	if utils.S3OpenAcl == "1" {
+		putObjectInput.ACL = aws.String(s3.ObjectCannedACLPublicRead)
+	}
+	fmt.Printf("put object input: %+v\n", putObjectInput)
+	_, e = client.PutObject(putObjectInput)
+	if e != nil {
+		err = fmt.Errorf("put object err: %s", e.Error())
+		return
+	}
+	resourceUrl = resourceHost + path
+	if utils.ResourceProxyUrl != "" {
+		resourceUrl = utils.ResourceProxyUrl + path
+	}
+	return
+}
+
+func (m *S3Oss) GetUploadToken() (token OssToken, err error) {
+	token.Endpoint = utils.S3Endpoint
+	token.AccessKeyId = utils.S3AccessKeyId
+	token.AccessKeySecret = utils.S3AccessKeySecret
+	token.RegionId = utils.S3Region
+	token.Bucketname = utils.S3BucketName
+	token.ImgHost = utils.S3Host
+	token.Port = utils.S3EndpointPort
+	hostStyle := true // 默认true, 使用`endpoint/bucket_name`这种HOST格式
+	if utils.S3ForceStyle == "false" {
+		hostStyle = false
+	}
+	token.S3ForceStyle = hostStyle
+	token.S3Protocol = utils.S3Protocol
+	return
+}

+ 55 - 2
services/chart_classify.go

@@ -7,6 +7,7 @@ import (
 	"eta/eta_forum_admin/utils"
 	"fmt"
 	"strconv"
+	"strings"
 	"time"
 )
 
@@ -195,6 +196,7 @@ func AddChartClassify(chartClassifyName string, parentId, source int, lang strin
 	}
 	// 查询level值
 	level := 0
+	levelPath := ""
 	if parentId > 0 {
 		parentClassify, tErr := models.GetChartClassifyById(parentId)
 		if tErr != nil {
@@ -208,6 +210,7 @@ func AddChartClassify(chartClassifyName string, parentId, source int, lang strin
 			return
 		}
 		level = parentClassify.Level
+		levelPath = parentClassify.LevelPath
 	}
 
 	//获取该层级下最大的排序数
@@ -228,10 +231,26 @@ func AddChartClassify(chartClassifyName string, parentId, source int, lang strin
 	classifyInfo.Sort = maxSort + 1
 	classifyInfo.Source = source
 
-	_, err = models.AddChartClassify(classifyInfo)
-	if err != nil {
+	classifyId, e := models.AddChartClassify(classifyInfo)
+	if e != nil {
+		errMsg = "保存分类失败"
+		err = errors.New("保存分类失败,Err:" + e.Error())
+		return
+	}
+	if parentId > 0 {
+		levelPath = fmt.Sprintf("%s%d,", levelPath, classifyId)
+	} else {
+		levelPath = fmt.Sprintf("%d,", classifyId)
+	}
+	classifyInfo.ChartClassifyId = int(classifyId)
+	classifyInfo.LevelPath = levelPath
+	e = classifyInfo.Update([]string{"LevelPath"})
+	if e != nil {
+		errMsg = "保存分类失败"
+		err = errors.New("保存分类失败,Err:" + e.Error())
 		return
 	}
+
 	return
 }
 
@@ -614,6 +633,40 @@ func moveChartClassify(parentClassifyInfo, edbClassifyInfo, prevClassify, nextCl
 					}
 				}
 			}*/
+
+			if oldParentId != parentClassifyId {
+				oldLevelPath := edbClassifyInfo.LevelPath
+				levelPath := fmt.Sprintf("%s%d,", parentClassifyInfo.LevelPath, edbClassifyInfo.ChartClassifyId)
+				edbClassifyInfo.LevelPath = levelPath
+				err = edbClassifyInfo.Update([]string{"LevelPath"})
+				if err != nil {
+					err = fmt.Errorf("修改失败,Err:" + err.Error())
+					return
+				}
+
+				//更新子分类的levelpath
+				tmpList, e := models.GetChartClassifyByLevelPath(oldLevelPath)
+				if e != nil {
+					err = fmt.Errorf("保存分类失败,Err:" + e.Error())
+					return
+				}
+				// 把原先的父级levePath,替换成最新的父级序列
+				for _, tmp := range tmpList {
+					//获取字符串前缀的位置
+					after, _ := strings.CutPrefix(tmp.LevelPath, oldLevelPath)
+					fmt.Println("after", after)
+					// 拼接字符串
+					if after != "" {
+						tmp.LevelPath = levelPath + after
+						tmp.ModifyTime = time.Now()
+						e = tmp.Update([]string{"LevelPath", "ModifyTime"})
+						if e != nil {
+							err = fmt.Errorf("修改子分类,Err:" + e.Error())
+							return
+						}
+					}
+				}
+			}
 		}
 	} else {
 		if chartInfo == nil {

+ 150 - 0
services/chart_collect_elastic.go

@@ -0,0 +1,150 @@
+package services
+
+import (
+	"eta/eta_forum_admin/models/chart_collect"
+	"eta/eta_forum_admin/services/elastic"
+	"eta/eta_forum_admin/utils"
+	"fmt"
+	"strconv"
+	"strings"
+)
+
+// EsAddOrEditChartCollectByChartInfoId 新增和修改ES中的收藏图表数据
+func EsAddOrEditChartCollectByChartInfoId(chartInfoId int) {
+	errMsg := make([]string, 0)
+	defer func() {
+		if len(errMsg) > 0 {
+			fmt.Println("新增和修改ES中的图表数据失败:", strings.Join(errMsg, "\n"))
+		}
+	}()
+
+	var condition string
+	var pars []interface{}
+	condition += " and a.chart_info_id = ? "
+	pars = append(pars, chartInfoId)
+
+	allList, err := chart_collect.GetChartCollectByChartInfoIdAndUserIdByCondition(condition, pars)
+	if err != nil {
+		errMsg = append(errMsg, fmt.Sprintf("EsAddOrEditChartCollectByChartInfoId Err:%s", err.Error()))
+		return
+	}
+	for _, v := range allList {
+		//添加es
+		v.CollectClassifyIds += ","
+		err = elastic.EsAddOrEditDataInterface(utils.CHART_COLLECT_INDEX_NAME, v.UserChartInfoId, v)
+		if err != nil {
+			errMsg = append(errMsg, fmt.Sprintf("图表收藏:%s,添加Es失败,err:%s", v.UserChartInfoId, err.Error()))
+		}
+
+	}
+	return
+}
+
+// EsAddOrEditChartCollectByChartInfoIdUserId 新增和修改ES中的收藏图表数据
+func EsAddOrEditChartCollectByChartInfoIdUserId(chartInfoId, userId int) {
+	errMsg := make([]string, 0)
+	defer func() {
+		if len(errMsg) > 0 {
+			fmt.Println("新增和修改ES中的图表数据失败:", strings.Join(errMsg, "\n"))
+		}
+	}()
+
+	var condition string
+	var pars []interface{}
+	condition += " and a.chart_info_id = ? and a.user_id = ? "
+	pars = append(pars, chartInfoId, userId)
+
+	allList, err := chart_collect.GetChartCollectByChartInfoIdAndUserIdByCondition(condition, pars)
+	if err != nil {
+		errMsg = append(errMsg, fmt.Sprintf("EsAddOrEditChartCollectByChartInfoId Err:%s", err.Error()))
+		return
+	}
+	for _, v := range allList {
+		//添加es
+		v.CollectClassifyIds += ","
+		fmt.Println(v)
+		err = elastic.EsAddOrEditDataInterface(utils.CHART_COLLECT_INDEX_NAME, v.UserChartInfoId, v)
+		if err != nil {
+			errMsg = append(errMsg, fmt.Sprintf("图表收藏:%s,添加Es失败,err:%s", v.UserChartInfoId, err.Error()))
+		}
+
+	}
+	return
+}
+
+// EsDeleteChartCollectByChartInfoIdUserId 取消收藏
+func EsDeleteChartCollectByChartInfoIdUserId(chartInfoId, userId int) {
+	var err error
+	defer func() {
+		if err != nil {
+			fmt.Println("删除ES中的图表数据失败:", err.Error())
+		}
+	}()
+	//添加es
+	// 先删除,再新增
+	userChartInfoId := strconv.Itoa(userId) + "_" + strconv.Itoa(chartInfoId)
+	err = elastic.EsDeleteDataV2(utils.CHART_COLLECT_INDEX_NAME, userChartInfoId)
+	if err != nil {
+		err = fmt.Errorf(fmt.Sprintf("图表收藏:%s,删除Es失败,err:%s", userChartInfoId, err.Error()))
+		return
+	}
+	var condition string
+	var pars []interface{}
+	condition += " and a.chart_info_id = ? and a.user_id = ? "
+	pars = append(pars, chartInfoId, userId)
+
+	allList, err := chart_collect.GetChartCollectByChartInfoIdAndUserIdByCondition(condition, pars)
+	if err != nil {
+		return
+	}
+	for _, v := range allList {
+		//添加es
+		v.CollectClassifyIds += ","
+		err = elastic.EsAddOrEditDataInterface(utils.CHART_COLLECT_INDEX_NAME, v.UserChartInfoId, v)
+		if err != nil {
+			err = fmt.Errorf("图表收藏:%s,添加Es失败,err:%s", v.UserChartInfoId, err.Error())
+			return
+		}
+	}
+
+	return
+}
+
+// EsDeleteChartCollectByChartInfoIdUserIds 删除图表时,删除图表的收藏记录
+func EsDeleteChartCollectByChartInfoIdUserIds(userChartInfoIds []string) {
+	if len(userChartInfoIds) == 0 {
+		return
+	}
+	errMsg := make([]string, 0)
+	defer func() {
+		if len(errMsg) > 0 {
+			fmt.Println("EsDeleteMyChartInfoByMyChartIds, 删除ES中的我的图表数据失败:", strings.Join(errMsg, "\n"))
+		}
+	}()
+	for _, v := range userChartInfoIds {
+		id := v
+		if e := elastic.EsDeleteDataV2(utils.CHART_COLLECT_INDEX_NAME, id); e != nil {
+			errMsg = append(errMsg, fmt.Sprintf("myChartId:%d,删除Es失败,err:%s", v, e.Error()))
+		}
+	}
+	return
+}
+
+func AddAllChartCollectData() {
+	var condition string
+	var pars []interface{}
+
+	allList, err := chart_collect.GetChartCollectByChartInfoIdAndUserIdByCondition(condition, pars)
+	if err != nil {
+		return
+	}
+	for _, v := range allList {
+		//添加es
+		v.CollectClassifyIds += ","
+		err = elastic.EsAddOrEditDataInterface(utils.CHART_COLLECT_INDEX_NAME, v.UserChartInfoId, v)
+		if err != nil {
+			err = fmt.Errorf("图表收藏:%s,添加Es失败,err:%s", v.UserChartInfoId, err.Error())
+			return
+		}
+	}
+}

+ 122 - 0
services/eta_business/eta_business.go

@@ -0,0 +1,122 @@
+package eta_business
+
+import (
+	"eta/eta_forum_admin/models/eta_business"
+	"eta/eta_forum_admin/models/system"
+	"eta/eta_forum_admin/services/alarm_msg"
+	"eta/eta_forum_admin/utils"
+	"fmt"
+	"strings"
+	"time"
+)
+
+// CheckEtaBusinessOperateAuth 校验ETA商家操作权限
+func CheckEtaBusinessOperateAuth(roleCode string) (ok bool, err error) {
+	confKey := "eta_business_auth_role"
+	conf, e := system.GetCrmConfigDetailByCode(confKey)
+	if e != nil {
+		err = fmt.Errorf("获取权限配置失败, Err: %s", e.Error())
+		return
+	}
+	if conf.ConfigValue == "" {
+		err = fmt.Errorf("权限配置信息有误")
+		return
+	}
+	authArr := strings.Split(conf.ConfigValue, ",")
+	if utils.InArrayByStr(authArr, roleCode) {
+		ok = true
+	}
+	return
+}
+
+// UpdateEtaBusinessAfterSigning 签约后的后置操作
+func UpdateEtaBusinessAfterSigning(businessId int) (err error) {
+	if businessId <= 0 {
+		return
+	}
+	defer func() {
+		if err != nil {
+			tips := "签约后更新商家信息失败, Err: " + err.Error()
+			utils.FileLog.Info("%s", tips)
+			go alarm_msg.SendAlarmMsg(tips, 3)
+		}
+	}()
+
+	businessOb := new(eta_business.EtaBusiness)
+	business, e := businessOb.GetItemById(businessId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			return
+		}
+		err = fmt.Errorf("获取商家信息失败, Err: %s", e.Error())
+		return
+	}
+
+	contracts := make([]*eta_business.EtaBusinessContract, 0)
+	{
+		ob := new(eta_business.EtaBusinessContract)
+		cond := fmt.Sprintf(` AND %s = ?`, eta_business.EtaBusinessContractColumns.EtaBusinessId)
+		pars := make([]interface{}, 0)
+		pars = append(pars, businessId)
+		// 按签约时间倒序
+		list, e := ob.GetItemsByCondition(cond, pars, []string{}, fmt.Sprintf("%s DESC", eta_business.EtaBusinessContractColumns.SigningTime))
+		if e != nil {
+			err = fmt.Errorf("获取商家合同列表失败, Err: " + e.Error())
+			return
+		}
+		contracts = list
+	}
+	if len(contracts) == 0 {
+		// 重置签约信息
+		cols := []string{"ContractId", "SigningStatus", "SigningTime", "ExpiredTime", "ModifyTime"}
+		business.ContractId = 0
+		business.SigningStatus = eta_business.EtaBusinessSigningStatusWait
+		business.SigningTime = time.Time{}
+		business.ExpiredTime = time.Time{}
+		business.ModifyTime = time.Now().Local()
+		if e = business.Update(cols); e != nil {
+			err = fmt.Errorf("更新商家信息失败, Err: %s", e.Error())
+		}
+		return
+	}
+
+	strToday := time.Now().Format(utils.FormatDate)
+	today, _ := time.ParseInLocation(utils.FormatDate, strToday, time.Local)
+	using := false // 是否在任一存续期内
+	for _, c := range contracts {
+		// 当前合约
+		if today.Equal(c.SigningTime) || today.Equal(c.ExpiredTime) || (today.After(c.SigningTime) && today.Before(c.ExpiredTime)) {
+			business.ContractId = c.EtaBusinessContractId
+			business.SigningTime = c.SigningTime
+			business.ExpiredTime = c.ExpiredTime
+			business.ModifyTime = time.Now().Local()
+			// 是否为首次签约
+			if c.IsFirst == 1 {
+				business.SigningStatus = eta_business.EtaBusinessSigningStatusFirst
+			} else {
+				business.SigningStatus = eta_business.EtaBusinessSigningStatusContinue
+			}
+			using = true
+			break
+		}
+	}
+
+	// 不存在任一合同期内, 取签约时间最后的合同(已按时间排序)
+	if !using && len(contracts) > 0 {
+		business.ContractId = contracts[0].EtaBusinessContractId
+		business.SigningTime = contracts[0].SigningTime
+		business.ExpiredTime = contracts[0].ExpiredTime
+		if contracts[0].IsFirst == 1 {
+			business.SigningStatus = eta_business.EtaBusinessSigningStatusFirst
+		} else {
+			business.SigningStatus = eta_business.EtaBusinessSigningStatusTerminate
+		}
+		business.ModifyTime = time.Now().Local()
+	}
+
+	cols := []string{"ContractId", "SigningStatus", "SigningTime", "ExpiredTime", "ModifyTime"}
+	if e = business.Update(cols); e != nil {
+		err = fmt.Errorf("更新商家信息失败, Err: %s", e.Error())
+	}
+	return
+}

+ 46 - 0
services/eta_business/eta_business_menu.go

@@ -0,0 +1,46 @@
+package eta_business
+
+import (
+	"eta/eta_forum_admin/models/eta_business"
+)
+
+// GetMenuTreeRecursive 递归菜单树
+func GetMenuTreeRecursive(list []*eta_business.EtaBusinessMenuItem, parentId int) []*eta_business.EtaBusinessMenuItem {
+	res := make([]*eta_business.EtaBusinessMenuItem, 0)
+	for _, v := range list {
+		if v.ParentId == parentId {
+			v.Children = GetMenuTreeRecursive(list, v.MenuId)
+			res = append(res, v)
+		}
+	}
+	return res
+}
+
+// GetMenuChildrenIdsRecursive 遍历子菜单IDs
+func GetMenuChildrenIdsRecursive(list []*eta_business.EtaBusinessMenu, parentId int) []int {
+	res := make([]int, 0)
+	for _, v := range list {
+		if v.ParentId == parentId {
+			ids := GetMenuChildrenIdsRecursive(list, v.MenuId)
+			res = append(res, v.MenuId)
+			res = append(res, ids...)
+		}
+	}
+	return res
+}
+
+// GetMenuParentsRecursive 遍历子菜单的所有父级菜单
+func GetMenuParentsRecursive(list []*eta_business.EtaBusinessMenu, parentId int) []*eta_business.EtaBusinessMenu {
+	res := make([]*eta_business.EtaBusinessMenu, 0)
+	for _, v := range list {
+		if v.MenuId == parentId {
+			res = append(res, v)
+			parents := make([]*eta_business.EtaBusinessMenu, 0)
+			if v.ParentId > 0 {
+				parents = GetMenuParentsRecursive(list, v.ParentId)
+			}
+			res = append(res, parents...)
+		}
+	}
+	return res
+}

+ 16 - 0
services/eta_business/user.go

@@ -0,0 +1,16 @@
+package eta_business
+
+import "eta/eta_forum_admin/utils"
+
+// 校验当前操作员是否具有联系人权限是否有操作权限
+func CheckBusinessUserButton(roleTypeCode string, itemSellerId, sysUserId int) (ok bool) {
+	if roleTypeCode == utils.ROLE_TYPE_CODE_ADMIN || roleTypeCode == utils.ROLE_TYPE_CODE_FICC_ADMIN {
+		ok = true
+		return
+	}
+	if sysUserId == itemSellerId {
+		ok = true
+		return
+	}
+	return
+}

+ 214 - 0
services/eta_forum_hub/eta_trial.go

@@ -0,0 +1,214 @@
+package eta_forum_hub
+
+import (
+	"encoding/json"
+	"eta/eta_forum_admin/models"
+	"eta/eta_forum_admin/models/eta_trial"
+	"eta/eta_forum_admin/services/alarm_msg"
+	"eta/eta_forum_admin/utils"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+	"strings"
+	"time"
+)
+
+const (
+	LoginAuthCodeSource = 1
+)
+
+// MiddleServerResultData 中间服务响应体
+type MiddleServerResultData struct {
+	Code   int         `description:"状态码"`
+	Msg    string      `description:"提示信息"`
+	Data   interface{} `description:"返回数据"`
+	ErrMsg string      `description:"错误信息,不用返回给前端,只是做日志记录"`
+}
+
+// AddEtaTrialAdminReq 体验版用户添加手工权限
+type AddEtaTrialAdminReq struct {
+	UserName    string `description:"客户名称"`
+	Password    string
+	Account     string
+	CompanyName string `description:"客户公司姓名"`
+	Position    string `description:"职位"`
+	Mobile      string `description:"手机号"`
+	Enabled     int    `description:"1:有效,0:禁用"`
+	SellerId    int    `description:"销售id"`
+	Seller      string `description:"销售员名称"`
+	CreateTime  time.Time
+	ModifyTime  time.Time
+}
+
+type EnabledEtaTrialAdminReq struct {
+	Mobile string `description:"手机号"`
+}
+
+// AddEtaTrialAdmin CRM_ETA服务-体验版用户添加手工权限
+func AddEtaTrialAdmin(req *eta_trial.EtaTrial) (err error) {
+	// 体验版无测试环境
+	//if utils.RunMode != "release" {
+	//	return
+	//}
+	defer func() {
+		if err != nil {
+			alarm_msg.SendAlarmMsg(fmt.Sprintf("AddEtaTrialAdmin, 新增试用客户手工录入权限失败, ErrMsg: %s", err.Error()), 3)
+		}
+	}()
+	param := AddEtaTrialAdminReq{
+		UserName:    req.UserName,
+		Password:    req.Password,
+		Account:     req.Account,
+		CompanyName: req.CompanyName,
+		Position:    req.Position,
+		Mobile:      req.Mobile,
+		Enabled:     req.Enabled,
+		CreateTime:  req.CreateTime,
+		ModifyTime:  req.ModifyTime,
+	}
+	reqJson, e := json.Marshal(param)
+	if e != nil {
+		err = fmt.Errorf("data json marshal err: %s", e.Error())
+		return
+	}
+
+	_, resultByte, err := post(string(reqJson), "/v1/eta_trial/admin/add")
+	resp := new(models.BaseResponse)
+	err = json.Unmarshal(resultByte, &resp)
+	if err != nil {
+		return
+	}
+	if resp.Ret != 200 {
+		err = fmt.Errorf("result: %s, errmsg: %s", resp.Msg, resp.ErrMsg)
+		return
+	}
+	return
+}
+
+// EnabledEtaTrialAdmin CRM_ETA服务-体验版用户添加手工权限
+func EnabledEtaTrialAdmin(mobile string) (err error) {
+	// 体验版无测试环境
+	//if utils.RunMode != "release" {
+	//	return
+	//}
+	defer func() {
+		if err != nil {
+			alarm_msg.SendAlarmMsg(fmt.Sprintf("EnabledEtaTrialAdmin, 新增试用客户手工录入权限失败, ErrMsg: %s", err.Error()), 3)
+		}
+	}()
+	param := EnabledEtaTrialAdminReq{
+		Mobile: mobile,
+	}
+	reqJson, e := json.Marshal(param)
+	if e != nil {
+		err = fmt.Errorf("data json marshal err: %s", e.Error())
+		return
+	}
+
+	_, resultByte, err := post(string(reqJson), "/v1/eta_trial/admin/enabled")
+	resp := new(models.BaseResponse)
+	err = json.Unmarshal(resultByte, &resp)
+	if err != nil {
+		return
+	}
+	if resp.Ret != 200 {
+		err = fmt.Errorf("result: %s, errmsg: %s", resp.Msg, resp.ErrMsg)
+		return
+	}
+	return
+}
+
+type GetEtaTrialMobileCountReq struct {
+	Mobile string `description:"手机号"`
+}
+
+type EtaTrialMobileCount struct {
+	Count int
+}
+type GetEtaTrialMobileCountResp struct {
+	Ret         int
+	Msg         string
+	ErrMsg      string
+	ErrCode     string
+	Data        *EtaTrialMobileCount
+	Success     bool `description:"true 执行成功,false 执行失败"`
+	IsSendEmail bool `json:"-" description:"true 发送邮件,false 不发送邮件"`
+	IsAddLog    bool `json:"-" description:"true 新增操作日志,false 不新增操作日志" `
+}
+
+// GetEtaTrialMobileCount CRM_ETA服务-体验版用户添加手工权限
+func GetEtaTrialMobileCount(mobile string) (count int, err error) {
+	// 体验版无测试环境
+	//if utils.RunMode != "release" {
+	//	return
+	//}
+	defer func() {
+		if err != nil {
+			alarm_msg.SendAlarmMsg(fmt.Sprintf("GetEtaTrialMobileCount, 新增试用客户手工录入权限失败, ErrMsg: %s", err.Error()), 3)
+		}
+	}()
+	param := GetEtaTrialMobileCountReq{
+		Mobile: mobile,
+	}
+	reqJson, e := json.Marshal(param)
+	if e != nil {
+		err = fmt.Errorf("data json marshal err: %s", e.Error())
+		return
+	}
+
+	_, resultByte, err := post(string(reqJson), "/v1/eta_trial/mobile/count")
+	resp := new(GetEtaTrialMobileCountResp)
+	err = json.Unmarshal(resultByte, &resp)
+	if err != nil {
+		return
+	}
+	if resp.Ret != 200 {
+		err = fmt.Errorf("result: %s, errmsg: %s", resp.Msg, resp.ErrMsg)
+		return
+	}
+	count = resp.Data.Count
+	return
+}
+
+// post
+func post(paramStr string, urlStr string) (resp *models.BaseResponse, result []byte, err error) {
+	if utils.ETA_FORUM_HUB_URL == "" {
+		err = fmt.Errorf("ETA社区桥接服务地址为空")
+		return
+	}
+	postUrl := utils.ETA_FORUM_HUB_URL + urlStr
+	result, err = HttpPost(postUrl, paramStr, "application/json")
+	if err != nil {
+		err = fmt.Errorf("调用ETA社区桥接服务接口失败 error:%s", err.Error())
+		return
+	}
+	err = json.Unmarshal(result, &resp)
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+func HttpPost(url, postData string, params ...string) ([]byte, error) {
+	body := ioutil.NopCloser(strings.NewReader(postData))
+	client := &http.Client{}
+	req, err := http.NewRequest("POST", url, body)
+	if err != nil {
+		return nil, err
+	}
+	contentType := "application/x-www-form-urlencoded;charset=utf-8"
+	if len(params) > 0 && params[0] != "" {
+		contentType = params[0]
+	}
+	req.Header.Set("Content-Type", contentType)
+	req.Header.Set("authorization", utils.MD5(utils.ETA_FORUM_HUB_NAME_EN+utils.ETA_FORUM_HUB_MD5_KEY))
+	resp, err := client.Do(req)
+	if err != nil {
+		return nil, err
+	}
+	defer resp.Body.Close()
+	b, err := ioutil.ReadAll(resp.Body)
+	utils.FileLog.Debug("HttpPost:" + string(b))
+	return b, err
+}

+ 108 - 0
services/eta_training_video/eta_training_video.go

@@ -0,0 +1,108 @@
+package eta_training_video
+
+import (
+	"eta/eta_forum_admin/models/eta_training_video"
+	"eta/eta_forum_admin/utils"
+	"fmt"
+	"strconv"
+	"strings"
+)
+
+// FormatVideosToVideoItems 格式化视频列表
+func FormatVideosToVideoItems(list []*eta_training_video.EtaTrainingVideo) (items []*eta_training_video.EtaTrainingVideoItem, err error) {
+	items = make([]*eta_training_video.EtaTrainingVideoItem, 0)
+	if len(list) == 0 {
+		return
+	}
+
+	// 获取分类
+	classifyMap := make(map[int]*eta_training_video.EtaTrainingVideoClassify)
+	{
+		classifyOB := new(eta_training_video.EtaTrainingVideoClassify)
+		classifies, e := classifyOB.GetItemsByCondition(``, make([]interface{}, 0), []string{}, "")
+		if e != nil {
+			err = fmt.Errorf("获取分类列表失败, Err: %s", e.Error())
+			return
+		}
+		for _, v := range classifies {
+			classifyMap[v.EtaTrainingVideoClassifyId] = v
+		}
+	}
+
+	// 获取标签
+	tagMap := make(map[int]*eta_training_video.EtaTrainingVideoTag)
+	{
+		tagOB := new(eta_training_video.EtaTrainingVideoTag)
+		tags, e := tagOB.GetItemsByCondition(``, make([]interface{}, 0), []string{}, "")
+		if e != nil {
+			err = fmt.Errorf("获取标签列表失败, Err: %s", e.Error())
+			return
+		}
+		for _, v := range tags {
+			tagMap[v.EtaTrainingVideoTagId] = v
+		}
+	}
+
+	for _, v := range list {
+		b := new(eta_training_video.EtaTrainingVideoItem)
+		b.VideoId = v.EtaTrainingVideoId
+		b.VideoCode = v.VideoCode
+		b.Title = v.Title
+		b.Introduce = v.Introduce
+		b.CoverImg = v.CoverImg
+		b.VideoUrl = v.VideoUrl
+		b.PublishState = v.PublishState
+		b.PublishTime = utils.TimeTransferString(utils.FormatDateTime, v.PublishTime)
+		b.ViewTotal = v.ViewTotal
+		b.CreateTime = utils.TimeTransferString(utils.FormatDateTime, v.CreateTime)
+		b.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, v.ModifyTime)
+
+		// 分类
+		if v.ClassifyIds != "" {
+			strClassifyIdArr := strings.Split(v.ClassifyIds, ",")
+			if len(strClassifyIdArr) > 0 {
+				arr := make([]*eta_training_video.EtaTrainingVideoClassifyItem, 0)
+				for _, s := range strClassifyIdArr {
+					id, _ := strconv.Atoi(s)
+					c := classifyMap[id]
+					if c != nil {
+						arr = append(arr, &eta_training_video.EtaTrainingVideoClassifyItem{
+							ClassifyId:   c.EtaTrainingVideoClassifyId,
+							ClassifyName: c.ClassifyName,
+							ParentId:     c.ParentId,
+							Sort:         c.Sort,
+						})
+					}
+				}
+				tree := GetClassifyTreeRecursive(arr, 0)
+				if len(tree) > 0 {
+					b.Classify = tree[0]
+				}
+			}
+		}
+
+		// 标签
+		if v.TagIds != "" {
+			strTagIdArr := strings.Split(v.TagIds, ",")
+			if len(strTagIdArr) > 0 {
+				b.Tags = make([]*eta_training_video.EtaTrainingVideoTagItem, 0)
+				for _, s := range strTagIdArr {
+					id, _ := strconv.Atoi(s)
+					t := tagMap[id]
+					if t != nil {
+						b.Tags = append(b.Tags, &eta_training_video.EtaTrainingVideoTagItem{
+							TagId:      t.EtaTrainingVideoTagId,
+							TagName:    t.TagName,
+							VideoTotal: t.VideoTotal,
+							CreateTime: utils.TimeTransferString(utils.FormatDateTime, t.CreateTime),
+							ModifyTime: utils.TimeTransferString(utils.FormatDateTime, t.ModifyTime),
+						})
+					}
+				}
+			}
+		}
+
+		items = append(items, b)
+	}
+	return
+}

+ 46 - 0
services/eta_training_video/eta_training_video_classify.go

@@ -0,0 +1,46 @@
+package eta_training_video
+
+import (
+	"eta/eta_forum_admin/models/eta_training_video"
+)
+
+// GetClassifyTreeRecursive 递归菜单树
+func GetClassifyTreeRecursive(list []*eta_training_video.EtaTrainingVideoClassifyItem, parentId int) []*eta_training_video.EtaTrainingVideoClassifyItem {
+	res := make([]*eta_training_video.EtaTrainingVideoClassifyItem, 0)
+	for _, v := range list {
+		if v.ParentId == parentId {
+			v.Children = GetClassifyTreeRecursive(list, v.ClassifyId)
+			res = append(res, v)
+		}
+	}
+	return res
+}
+
+// GetClassifyChildrenIdsRecursive 遍历子菜单IDs
+func GetClassifyChildrenIdsRecursive(list []*eta_training_video.EtaTrainingVideoClassify, parentId int) []int {
+	res := make([]int, 0)
+	for _, v := range list {
+		if v.ParentId == parentId {
+			ids := GetClassifyChildrenIdsRecursive(list, v.EtaTrainingVideoClassifyId)
+			res = append(res, v.EtaTrainingVideoClassifyId)
+			res = append(res, ids...)
+		}
+	}
+	return res
+}
+
+// GetClassifyParentsRecursive 遍历子分类的所有父级分类
+func GetClassifyParentsRecursive(list []*eta_training_video.EtaTrainingVideoClassify, parentId int) []*eta_training_video.EtaTrainingVideoClassify {
+	res := make([]*eta_training_video.EtaTrainingVideoClassify, 0)
+	for _, v := range list {
+		if v.EtaTrainingVideoClassifyId == parentId {
+			res = append(res, v)
+			parents := make([]*eta_training_video.EtaTrainingVideoClassify, 0)
+			if v.ParentId > 0 {
+				parents = GetClassifyParentsRecursive(list, v.ParentId)
+			}
+			res = append(res, parents...)
+		}
+	}
+	return res
+}

+ 254 - 0
services/eta_trial.go

@@ -0,0 +1,254 @@
+package services
+
+import (
+	"eta/eta_forum_admin/models/company"
+	"eta/eta_forum_admin/models/eta_trial"
+	"eta/eta_forum_admin/models/system"
+	"eta/eta_forum_admin/services/alarm_msg"
+	"eta/eta_forum_admin/services/eta_forum_hub"
+	"eta/eta_forum_admin/utils"
+	"fmt"
+	"github.com/mozillazg/go-pinyin"
+	"strconv"
+	"time"
+)
+
+// UpdateOldTrialUsersManualAuth 更新原试用客户手工录入权限(一次性)
+func UpdateOldTrialUsersManualAuth() (err error) {
+	// 从ETA试用平台的edb库中读取手工数据分类
+	classifyList, e := eta_trial.GetETATrialManualClassify()
+	if e != nil {
+		fmt.Println(e.Error())
+		return
+	}
+	classifyIds := make([]int, 0)
+	for i := range classifyList {
+		classifyIds = append(classifyIds, classifyList[i].ClassifyId)
+	}
+
+	// 查询未分配手工权限的试用客户Ids
+	userList, e := eta_trial.GetNoAuthTrialUserIds()
+	if e != nil {
+		fmt.Println(e.Error())
+		return
+	}
+	if len(userList) == 0 {
+		fmt.Println("无客户需要更新手工权限")
+		return
+	}
+	fmt.Println("classifyIds", classifyIds)
+
+	// 遍历试用客户, ETA试用平台data库新增manual_user用户关联、manual_user_classify权限关联
+	for i := range userList {
+		fmt.Printf("用户%d-%s正在更新\n", userList[i].AdminId, userList[i].RealName)
+		e = eta_trial.InsertTrialUserManualAuth(userList[i].AdminId, userList[i].RealName, classifyIds)
+		if e != nil {
+			fmt.Println(e.Error())
+			return
+		}
+		fmt.Println("更新成功")
+		//break // 试一个先
+	}
+	return
+}
+
+// CreateTrialUserManualAuth 新增试用客户手工录入权限
+// 鉴于DB注册时即使不同库也不能同名的情况, 以下均使用原生SQL
+func CreateTrialUserManualAuth(adminId int, adminRealName string) (err error) {
+	defer func() {
+		if err != nil {
+			alarm_msg.SendAlarmMsg(fmt.Sprintf("新增试用客户手工录入权限失败, ErrMsg: %s", err.Error()), 3)
+		}
+	}()
+
+	// 从ETA试用平台的edb库中读取手工数据分类
+	classifyList, e := eta_trial.GetETATrialManualClassify()
+	if e != nil {
+		err = fmt.Errorf("获取手工数据分类失败, Err: %s", e.Error())
+		return
+	}
+	classifyIds := make([]int, 0)
+	for i := range classifyList {
+		classifyIds = append(classifyIds, classifyList[i].ClassifyId)
+	}
+
+	// 新增用户及权限
+	e = eta_trial.InsertTrialUserManualAuth(adminId, adminRealName, classifyIds)
+	if e != nil {
+		err = fmt.Errorf("新增手工数据权限失败, Err: %s", e.Error())
+		return
+	}
+	return
+}
+
+// ApprovalApply 同意审批
+func ApprovalApply(approvalId int) (err error) {
+	defer func() {
+		if err != nil {
+			tips := fmt.Sprintf("ETA试用-自动审批失败, ApprovalId: %d, Err: %s", approvalId, err.Error())
+			utils.FileLog.Info("%s", tips)
+			go alarm_msg.SendAlarmMsg(tips, 3)
+		}
+	}()
+
+	// 获取审批
+	approval, e := eta_trial.GetETATrialApprovalById(approvalId)
+	if e != nil {
+		err = fmt.Errorf("获取审批失败, Err: %s", e.Error())
+		return
+	}
+
+	// 更新审批
+	if e = eta_trial.ApprovalTrial(approvalId); e != nil {
+		err = fmt.Errorf("更新审批失败, Err: %s", e.Error())
+		return
+	}
+
+	// 如果是新客户申请就新增,旧客户更改账号状态即可
+	if approval.ApplyMethod == 2 {
+		if e = eta_trial.UpdateETATrialEnable(approval.Mobile); e != nil {
+			err = fmt.Errorf("启用失败, Err: %s", e.Error())
+			return
+		}
+		if e = eta_forum_hub.EnabledEtaTrialAdmin(approval.Mobile); e != nil {
+			err = fmt.Errorf("启用用户失败, Err: %s", e.Error())
+			return
+		}
+	} else {
+		//新增客户
+		newItem := eta_trial.EtaTrial{
+			UserName:    approval.UserName,
+			CompanyName: approval.CompanyName,
+			Position:    approval.Position,
+			Mobile:      approval.Mobile,
+			Enabled:     1,
+			SellerId:    approval.SellerId,
+			Seller:      approval.Seller,
+			CreateTime:  time.Now(),
+			ModifyTime:  time.Now(),
+		}
+
+		strResult := ""
+		a := pinyin.NewArgs()
+		rows := pinyin.Pinyin(newItem.UserName, a)
+		for i := 0; i < len(rows); i++ {
+			strResult += rows[i][0]
+		}
+
+		//若非中文
+		if strResult == "" {
+			strResult = newItem.UserName
+		}
+		list, e := eta_trial.GetETATrialByAccount(strResult)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			err = fmt.Errorf("获取ETA试用账号失败, Err: %s", e.Error())
+			return
+		}
+		if list != nil && len(list) > 0 {
+			newItem.Account = strResult + strconv.Itoa(len(list))
+		} else {
+			newItem.Account = strResult
+		}
+
+		// 初始密码固定
+		newItem.Password = "123456a"
+
+		_, e = eta_trial.AddETATrial(&newItem)
+		if e != nil {
+			err = fmt.Errorf("新增ETA试用账号失败, Err: %s", e.Error())
+			return
+		}
+
+		//// 新增至试用平台的Admin
+		//adminItem, e := eta_trial.GetSysUserByRoleAndDepartment("试用", "ETA试用客户")
+		//if e != nil {
+		//	err = fmt.Errorf("获取ETA试用admin失败, Err: %s", e.Error())
+		//	return
+		//}
+		//
+		//admin := new(eta_trial.ETATrialAdmin)
+		//admin.AdminName = newItem.Account
+		//admin.RealName = newItem.UserName
+		//admin.Password = utils.MD5(newItem.Password)
+		//admin.LastUpdatedPasswordTime = time.Now().Format(utils.FormatDateTime)
+		//admin.Enabled = 1
+		//admin.LastLoginTime = time.Now().Format(utils.FormatDateTime)
+		//admin.CreatedTime = time.Now()
+		//admin.LastUpdatedTime = time.Now().Format(utils.FormatDateTime)
+		//admin.Mobile = newItem.Mobile
+		//admin.RoleType = 0
+		//admin.RoleId = adminItem.RoleId
+		//admin.RoleName = "试用"
+		//admin.RoleTypeCode = "管理员"
+		//admin.DepartmentId = adminItem.DepartmentId
+		//admin.DepartmentName = "ETA试用客户"
+		//admin.Role = "admin"
+		//admin.Position = approval.Position
+		//
+		//newId, e := eta_trial.AddAdmin(admin)
+		//if e != nil {
+		//	err = fmt.Errorf("新增ETA试用admin失败, Err: %s", e.Error())
+		//	return
+		//}
+		//
+		//// 新增试用客户手工权限
+		//go func() {
+		//	//_ = CreateTrialUserManualAuth(newId, admin.RealName)
+		//	_ = eta_forum_hub.EtaTrialManualUserAddAuth(newId, admin.RealName)
+		//}()
+		err = eta_forum_hub.AddEtaTrialAdmin(&newItem)
+		if err != nil {
+			err = fmt.Errorf("新增试用用户失败")
+			return
+		}
+	}
+
+	// 推送消息给销售
+	msgItem := new(company.CompanyApprovalMessage)
+	msgItem.CreateUserId = 0 // 系统自动创建的消息
+	msgItem.ReceiveUserId = approval.SellerId
+	msgItem.MessageStatus = 0
+	msgItem.Remark = approval.UserName + approval.ApprovalContent + "通过"
+	msgItem.Content = approval.UserName + approval.ApprovalContent + "通过"
+	msgItem.CompanyName = approval.CompanyName
+	msgItem.CreateTime = time.Now()
+	msgItem.ModifyTime = time.Now()
+	msgItem.CompanyApprovalId = approvalId
+	msgItem.OperationStatus = 1
+	msgItem.MessageType = 2    //1:申请消息,2:审批结果,3:文字消息
+	msgItem.SourceType = 9     //消息来源
+	msgItem.ApprovalStatus = 2 //审批状态,1:待审批,2:已审批,3:已驳回
+	err = company.AddCompanyApprovalMessage(msgItem)
+
+	// 更新审批消息(发给所有管理员的)为已读
+	//go func() {
+	//	cond := ` AND company_approval_id = ? AND source_type = ? AND message_status = ? AND receive_user_id = -1 `
+	//	pars := make([]interface{}, 0)
+	//	pars = append(pars, approvalId, 9, 0)
+	//	_ = company.ModifyCompanyApprovalMessageStatusByCond(cond, pars)
+	//}()
+	return
+}
+
+// 把crm中的销售ID转成成新的销售ID
+func InitEtaForumAdminId() (err error) {
+	defer func() {
+		if err != nil {
+			fmt.Println("InitEtaForumAdminId err: %s", err.Error())
+		}
+	}()
+	now := time.Now()
+	beforeTime := "2025-01-03" // 改成当前执行的时间,,修改这个时间之前的所有数据,避免替换后续新产生的数据
+	adminList, err := system.GetDifferentAdmin()
+	if err != nil {
+		return
+	}
+	for _, item := range adminList {
+		err = system.SetDifferentAdmin(item.AdminId, item.RealName, item.CrmAdminId, item.CrmRealName, now, beforeTime)
+		if err != nil {
+			return
+		}
+	}
+	fmt.Println("InitEtaForumAdminId Success 替换账号:", len(adminList), "个")
+	return
+}

+ 32 - 0
services/help_doc_classify.go

@@ -0,0 +1,32 @@
+package services
+
+import "eta/eta_forum_admin/models/help_doc"
+
+func helpDocClassifyHaveChild(allNode []*help_doc.HelpDocClassifyItems, node *help_doc.HelpDocClassifyItems) (childs []*help_doc.HelpDocClassifyItems, yes bool) {
+	for _, v := range allNode {
+		if v.ParentId == node.ClassifyId {
+			childs = append(childs, v)
+		}
+	}
+	if len(childs) > 0 {
+		yes = true
+	}
+	return
+}
+
+func HelpDocClassifyItemsMakeTree(allNode []*help_doc.HelpDocClassifyItems, node *help_doc.HelpDocClassifyItems) {
+	childs, _ := helpDocClassifyHaveChild(allNode, node) //判断节点是否有子节点并返回
+	if len(childs) > 0 {
+		node.Children = append(node.Children, childs[0:]...) //添加子节点
+		for _, v := range childs {                           //查询子节点的子节点,并添加到子节点
+			_, has := helpDocClassifyHaveChild(allNode, v)
+			if has {
+				HelpDocClassifyItemsMakeTree(allNode, v) //递归添加节点
+			} else {
+				v.Children = nil
+			}
+		}
+	} else {
+		node.Children = nil
+	}
+}

+ 461 - 0
services/minio.go

@@ -0,0 +1,461 @@
+package services
+
+import (
+	"context"
+	"errors"
+	"eta/eta_forum_admin/utils"
+	"fmt"
+	"github.com/minio/minio-go/v7"
+	"github.com/minio/minio-go/v7/pkg/credentials"
+	"log"
+	"time"
+)
+
+//func GetMinIOSTSToken() (item *Token, err error) {
+//	// MinIO服务的访问信息
+//	item = new(Token)
+//	//useSSL := false
+//	//if utils.MinIoUseSSL == "true" {
+//	//	useSSL = true
+//	//}
+//	// 创建MinIO客户端
+//	//minioClient, err := minio.New(utils.MinIoEndpoint, &minio.Options{
+//	//	Creds:  credentials.NewStaticV4(utils.MinIoAccessKeyId, utils.MinIoAccessKeySecret, ""),
+//	//	Secure: useSSL,
+//	//})
+//	//if err != nil {
+//	//	return nil, err
+//	//}
+//	// 设置STS凭证请求参数
+//	//policy := `{
+//	//    "Version": "2012-10-17",
+//	//    "Statement": [
+//	//        {
+//	//            "Sid": "",
+//	//            "Effect": "Allow",
+//	//            "Principal": {"AWS": "arn:aws:iam::1234567890:root"},
+//	//            "Action": "s3:GetObject",
+//	//            "Resource": "arn:aws:s3:::<YourBucketName>/*"
+//	//        }
+//	//    ]
+//	//}`
+//	//expiry := time.Hour * 24 // STS凭证的过期时间
+//	//获取STS凭证
+//	//stsCredentials, err := minioClient.PresignedPutObject(context.Background(), "etastatic", "myobject", expiry)
+//	//if err != nil {
+//	//	return
+//	//}
+//	item.AccessKeyId = utils.MinIoAccessKeyId
+//	item.SecretKeyId = utils.MinIoAccessKeySecret
+//	item.Endpoint = utils.MinIoEndpoint
+//	item.ImgHost = utils.MinIoImghost
+//	item.Bucketname = utils.MinIoBucketname
+//	item.UseSSL = utils.MinIoUseSSL
+//	item.RegionId = utils.MinIoRegion
+//	item.Port = utils.MinIoPort
+//	return
+//}
+
+//type Token struct {
+//	AccessKeyId string
+//	SecretKeyId string
+//	RegionId    string
+//	Bucketname  string
+//	Endpoint    string
+//	ImgHost     string
+//	UseSSL      string
+//	Port        string
+//}
+
+//func UploadMinIo() {
+//	ctx := context.Background()
+//	endpoint := "8.136.199.33:9000/"
+//	accessKeyID := "LfQ8uiJiLP7vLxjRrmNW"
+//	secretAccessKey := "IszGVHsNicJMQxHC46cYFtbrOiapo0ynwOIJ6c2R"
+//	useSSL := false
+//
+//	// Initialize minio client object.
+//	minioClient, err := minio.New(endpoint, &minio.Options{
+//		Creds:  credentials.NewStaticV4(accessKeyID, secretAccessKey, ""),
+//		Secure: useSSL,
+//	})
+//	if err != nil {
+//		log.Fatalln(err)
+//	}
+//
+//	// Make a new bucket called mymusic.
+//	bucketName := "etastatic"
+//	location := "/"
+//
+//	err = minioClient.MakeBucket(ctx, bucketName, minio.MakeBucketOptions{Region: location})
+//	if err != nil {
+//		// Check to see if we already own this bucket (which happens if you run this twice)
+//		exists, errBucketExists := minioClient.BucketExists(ctx, bucketName)
+//		if errBucketExists == nil && exists {
+//			log.Printf("We already own %s\n", bucketName)
+//		} else {
+//			log.Fatalln(err)
+//		}
+//	} else {
+//		log.Printf("Successfully created %s\n", bucketName)
+//	}
+//	//buckets, err := minioClient.ListBuckets(ctx)
+//	//for _, bucket := range buckets {
+//	//	fmt.Println(bucket)
+//	//}
+//	// Upload the zip file
+//	objectName := "1111.xlsx"
+//	filePath := "/Users/xi/Desktop/1111.xlsx"
+//	contentType := "application/xlsx"
+//
+//	// Upload the zip file with FPutObject
+//	info, err := minioClient.FPutObject(ctx, bucketName, objectName, filePath, minio.PutObjectOptions{ContentType: contentType})
+//	if err != nil {
+//		log.Fatalln(err)
+//	}
+//
+//	log.Printf("Successfully uploaded %s of size %d\n", objectName, info.Size)
+//}
+
+// UploadImgToMinIo 图片上传
+//func UploadImgToMinIo(fileName, filePath string) (string, error) {
+//	if utils.MinIoAccessKeyId == `` || utils.MinIoAccessKeySecret == `` {
+//		return "0", errors.New("MinIo信息未配置")
+//	}
+//
+//	ctx := context.Background()
+//	endpoint := utils.MinIoEndpoint
+//	accessKeyID := utils.MinIoAccessKeyId
+//	secretAccessKey := utils.MinIoAccessKeySecret
+//	useSSL := false
+//	if utils.MinIoUseSSL == "true" {
+//		useSSL = true
+//	}
+//	minioClient, err := minio.New(endpoint, &minio.Options{
+//		Creds:  credentials.NewStaticV4(accessKeyID, secretAccessKey, ""),
+//		Secure: useSSL,
+//	})
+//	if err != nil {
+//		log.Fatalln(err)
+//		return "1", err
+//	}
+//	bucketName := utils.MinIoBucketname
+//	// Check to see if we already own this bucket (which happens if you run this twice)
+//	exists, errBucketExists := minioClient.BucketExists(ctx, bucketName)
+//	if errBucketExists == nil && exists {
+//		log.Printf("We already own %s\n", bucketName)
+//	} else {
+//		log.Fatalln(err)
+//		return "2", err
+//	}
+//	path := utils.MinIoUploadDir + time.Now().Format("200601/20060102/")
+//	path += fileName
+//	// Upload the zip file with FPutObject
+//	//contentType := "application/xlsx"
+//	_, err = minioClient.FPutObject(ctx, bucketName, path, filePath, minio.PutObjectOptions{})
+//	if err != nil {
+//		log.Fatalln(err)
+//		return "3", err
+//	}
+//
+//	path = utils.MinIoImghost + path
+//	return path, err
+//}
+
+// UploadAudioToMinIo 音频上传
+//func UploadAudioToMinIo(fileName, filePath string) (string, error) {
+//	if utils.MinIoAccessKeyId == `` || utils.MinIoAccessKeySecret == `` {
+//		return "0", errors.New("MinIo信息未配置")
+//	}
+//
+//	ctx := context.Background()
+//	endpoint := utils.MinIoEndpoint
+//	accessKeyID := utils.MinIoAccessKeyId
+//	secretAccessKey := utils.MinIoAccessKeySecret
+//	useSSL := false
+//	if utils.MinIoUseSSL == "true" {
+//		useSSL = true
+//	}
+//	minioClient, err := minio.New(endpoint, &minio.Options{
+//		Creds:  credentials.NewStaticV4(accessKeyID, secretAccessKey, ""),
+//		Secure: useSSL,
+//	})
+//	if err != nil {
+//		log.Fatalln(err)
+//		return "1", err
+//	}
+//	bucketName := utils.MinIoBucketname
+//	// Check to see if we already own this bucket (which happens if you run this twice)
+//	exists, errBucketExists := minioClient.BucketExists(ctx, bucketName)
+//	if errBucketExists == nil && exists {
+//		log.Printf("We already own %s\n", bucketName)
+//	} else {
+//		log.Fatalln(err)
+//		return "2", err
+//	}
+//
+//	path := utils.MinIoUpload_Audio_Dir + time.Now().Format("200601/20060102/")
+//	path += fileName
+//
+//	// Upload the zip file with FPutObject
+//	//contentType := "application/xlsx"
+//	_, err = minioClient.FPutObject(ctx, bucketName, path, filePath, minio.PutObjectOptions{})
+//	if err != nil {
+//		log.Fatalln(err)
+//		return "3", err
+//	}
+//
+//	path = utils.MinIoImghost + path
+//	return path, err
+//}
+
+// UploadVideoToMinIo 视频上传
+//func UploadVideoToMinIo(filename, filePath, savePath string) error {
+//	if utils.MinIoAccessKeyId == `` || utils.MinIoAccessKeySecret == `` {
+//		return errors.New("MinIo信息未配置")
+//	}
+//	defer func() {
+//		os.Remove(filePath)
+//	}()
+//
+//	ctx := context.Background()
+//	endpoint := utils.MinIoEndpoint
+//	accessKeyID := utils.MinIoAccessKeyId
+//	secretAccessKey := utils.MinIoAccessKeySecret
+//	useSSL := false
+//	if utils.MinIoUseSSL == "true" {
+//		useSSL = true
+//	}
+//	minioClient, err := minio.New(endpoint, &minio.Options{
+//		Creds:  credentials.NewStaticV4(accessKeyID, secretAccessKey, ""),
+//		Secure: useSSL,
+//	})
+//	if err != nil {
+//		log.Fatalln(err)
+//		return err
+//	}
+//	bucketName := utils.MinIoBucketname
+//	// Check to see if we already own this bucket (which happens if you run this twice)
+//	exists, errBucketExists := minioClient.BucketExists(ctx, bucketName)
+//	if errBucketExists == nil && exists {
+//		log.Printf("We already own %s\n", bucketName)
+//	} else {
+//		log.Fatalln(err)
+//		return err
+//	}
+//
+//	//path := utils.Upload_Audio_Dir + time.Now().Format("200601/20060102/")
+//	//path += filename
+//	_, err = minioClient.FPutObject(ctx, bucketName, savePath, filePath, minio.PutObjectOptions{})
+//	if err != nil {
+//		log.Fatalln(err)
+//		return err
+//	}
+//	//path = utils.Imghost + path
+//	//return path,err
+//	return err
+//}
+
+// UploadFileToMinIo 上传文件
+//func UploadFileToMinIo(filename, filePath, savePath string) error {
+//	if utils.MinIoAccessKeyId == `` || utils.MinIoAccessKeySecret == `` {
+//		return errors.New("MinIo信息未配置")
+//	}
+//	defer func() {
+//		os.Remove(filePath)
+//	}()
+//	ctx := context.Background()
+//	endpoint := utils.MinIoEndpoint
+//	accessKeyID := utils.MinIoAccessKeyId
+//	secretAccessKey := utils.MinIoAccessKeySecret
+//	useSSL := false
+//	if utils.MinIoUseSSL == "true" {
+//		useSSL = true
+//	}
+//	minioClient, err := minio.New(endpoint, &minio.Options{
+//		Creds:  credentials.NewStaticV4(accessKeyID, secretAccessKey, ""),
+//		Secure: useSSL,
+//	})
+//	if err != nil {
+//		log.Fatalln(err)
+//		return err
+//	}
+//	bucketName := utils.MinIoBucketname
+//	// Check to see if we already own this bucket (which happens if you run this twice)
+//	exists, errBucketExists := minioClient.BucketExists(ctx, bucketName)
+//	if errBucketExists == nil && exists {
+//		log.Printf("We already own %s\n", bucketName)
+//	} else {
+//		log.Fatalln(err)
+//		return err
+//	}
+//	//path := utils.Upload_Audio_Dir + time.Now().Format("200601/20060102/")
+//	//path += filename
+//	_, err = minioClient.FPutObject(ctx, bucketName, savePath, filePath, minio.PutObjectOptions{})
+//	if err != nil {
+//		log.Fatalln(err)
+//		return err
+//	}
+//	//path = utils.Imghost + path
+//	//return path,err
+//	return err
+//}
+
+// UploadMinIoToDir 上传至hzchart
+//func UploadMinIoToDir(filename, filePath, uploadDir, fileDir string) (string, error) {
+//	if utils.MinIoAccessKeyId == `` || utils.MinIoAccessKeySecret == `` {
+//		return "0", errors.New("MinIo信息未配置")
+//	}
+//	ctx := context.Background()
+//	endpoint := utils.MinIoEndpoint
+//	accessKeyID := utils.MinIoAccessKeyId
+//	secretAccessKey := utils.MinIoAccessKeySecret
+//	useSSL := false
+//	if utils.MinIoUseSSL == "true" {
+//		useSSL = true
+//	}
+//	minioClient, err := minio.New(endpoint, &minio.Options{
+//		Creds:  credentials.NewStaticV4(accessKeyID, secretAccessKey, ""),
+//		Secure: useSSL,
+//	})
+//	if err != nil {
+//		log.Fatalln(err)
+//		return "1", err
+//	}
+//	bucketName := utils.MinIoBucketname
+//	// Check to see if we already own this bucket (which happens if you run this twice)
+//	exists, errBucketExists := minioClient.BucketExists(ctx, bucketName)
+//	if errBucketExists == nil && exists {
+//		log.Printf("We already own %s\n", bucketName)
+//	} else {
+//		log.Fatalln(err)
+//		return "2", err
+//	}
+//	if uploadDir == "" {
+//		uploadDir = utils.MinIoUploadDir
+//	}
+//	if fileDir == "" {
+//		fileDir = time.Now().Format("200601/20060102/")
+//	}
+//	path := uploadDir + fileDir
+//	path += filename
+//	_, err = minioClient.FPutObject(ctx, bucketName, path, filePath, minio.PutObjectOptions{})
+//	if err != nil {
+//		log.Fatalln(err)
+//		return "3", err
+//	}
+//	path = utils.MinIoImghost + path
+//	return path, err
+//}
+
+//func UploadImgToMinIoTest(fileName, filePath string) (string, error) {
+//	ctx := context.Background()
+//	endpoint := utils.Endpoint
+//	accessKeyID := utils.AccessKeyId
+//	secretAccessKey := utils.AccessKeySecret
+//	useSSL := false
+//	if utils.MinIoUseSSL == "true" {
+//		useSSL = true
+//	}
+//	minioClient, err := minio.New(endpoint, &minio.Options{
+//		Creds:  credentials.NewStaticV4(accessKeyID, secretAccessKey, ""),
+//		Secure: useSSL,
+//	})
+//	if err != nil {
+//		log.Fatalln(err)
+//		return "1", err
+//	}
+//	bucketName := utils.Bucketname
+//	// Check to see if we already own this bucket (which happens if you run this twice)
+//
+//	buckets, e := minioClient.ListBuckets(ctx)
+//	if e != nil {
+//		fmt.Println("ListBuckets: ", e.Error())
+//		return "", e
+//	}
+//	for k := range buckets {
+//		fmt.Println(k)
+//	}
+//
+//	exists, errBucketExists := minioClient.BucketExists(ctx, bucketName)
+//	fmt.Println("exists: ", exists)
+//	fmt.Println("errBucketExists: ", errBucketExists)
+//	if errBucketExists == nil && exists {
+//		log.Printf("We already own %s\n", bucketName)
+//	} else {
+//		log.Fatalln(err)
+//		return "2", err
+//	}
+//	path := utils.UploadDir + time.Now().Format("200601/20060102/")
+//	path += fileName
+//	// Upload the zip file with FPutObject
+//	//contentType := "application/xlsx"
+//	_, err = minioClient.FPutObject(ctx, bucketName, path, filePath, minio.PutObjectOptions{})
+//	if err != nil {
+//		log.Fatalln(err)
+//		return "3", err
+//	}
+//
+//	path = utils.Imghost + path
+//	return path, err
+//}
+
+type MinioOss struct{}
+
+// UploadFile 上传文件
+func (m *MinioOss) UploadFile(fileName, filePath, savePath string) (string, error) {
+	if utils.MinIoAccessKeyId == `` || utils.MinIoAccessKeySecret == `` {
+		return "0", errors.New("MinIo信息未配置")
+	}
+
+	ctx := context.Background()
+	// 此处兼容一下前后端endpoint不一致的情况, 前端用minio_endpoint后端用minio_back_endpoint, minio_back_endpoint为空则都取前者
+	endpoint := utils.MinIoEndpoint
+	if utils.MinIoBackEndpoint != "" {
+		endpoint = utils.MinIoBackEndpoint
+	}
+	accessKeyID := utils.MinIoAccessKeyId
+	secretAccessKey := utils.MinIoAccessKeySecret
+	useSSL := false
+	if utils.MinIoUseSSL == "true" {
+		useSSL = true
+	}
+	minioClient, err := minio.New(endpoint, &minio.Options{
+		Creds:  credentials.NewStaticV4(accessKeyID, secretAccessKey, ""),
+		Secure: useSSL,
+	})
+	if err != nil {
+		log.Fatalln(err)
+		return "1", err
+	}
+	bucketName := utils.MinIoBucketname
+	exists, errBucketExists := minioClient.BucketExists(ctx, bucketName)
+	if errBucketExists != nil || !exists {
+		err = fmt.Errorf("BucketExists: %v; err: %v", exists, errBucketExists)
+		return "2", err
+	}
+
+	path := savePath
+	if savePath == "" {
+		path = utils.MinIoUploadDir + time.Now().Format("200601/20060102/") + fileName
+	}
+	_, err = minioClient.FPutObject(ctx, bucketName, path, filePath, minio.PutObjectOptions{})
+	if err != nil {
+		log.Fatalln(err)
+		return "3", err
+	}
+	resourceUrl := utils.MinIoImghost + path
+	return resourceUrl, err
+}
+
+func (m *MinioOss) GetUploadToken() (token OssToken, err error) {
+	token.AccessKeyId = utils.MinIoAccessKeyId
+	token.SecretKeyId = utils.MinIoAccessKeySecret
+	token.Endpoint = utils.MinIoEndpoint
+	token.ImgHost = utils.MinIoImghost
+	token.Bucketname = utils.MinIoBucketname
+	token.UseSSL = utils.MinIoUseSSL
+	token.RegionId = utils.MinIoRegion
+	token.Port = utils.MinIoPort
+	return
+}

+ 297 - 0
services/oss.go

@@ -0,0 +1,297 @@
+package services
+
+import (
+	"encoding/json"
+	"errors"
+	"eta/eta_forum_admin/services/alarm_msg"
+	"eta/eta_forum_admin/utils"
+	"fmt"
+	"github.com/aliyun/aliyun-oss-go-sdk/oss"
+	"time"
+
+	"github.com/aliyun/alibaba-cloud-sdk-go/services/sts"
+)
+
+// UploadAliyunV2 图片上传到阿里云
+//func UploadAliyunV2(filename, filepath string) (string, error) {
+//	if utils.AccessKeyId == `` {
+//		return "0", errors.New("阿里云信息未配置")
+//	}
+//	client, err := oss.New(utils.Endpoint, utils.AccessKeyId, utils.AccessKeySecret)
+//	if err != nil {
+//		return "1", err
+//	}
+//	bucket, err := client.Bucket(utils.Bucketname)
+//	if err != nil {
+//		return "2", err
+//	}
+//	path := utils.UploadDir + time.Now().Format("200601/20060102/")
+//	path += filename
+//	err = bucket.PutObjectFromFile(path, filepath)
+//	if err != nil {
+//		return "3", err
+//	}
+//	path = utils.Imghost + path
+//	return path, err
+//}
+
+// UploadAudioAliyun 音频上传到阿里云
+//func UploadAudioAliyun(filename, filepath string) (string, error) {
+//	if utils.AccessKeyId == `` {
+//		return "0", errors.New("阿里云信息未配置")
+//	}
+//	client, err := oss.New(utils.Endpoint, utils.AccessKeyId, utils.AccessKeySecret)
+//	if err != nil {
+//		return "1", err
+//	}
+//	bucket, err := client.Bucket(utils.Bucketname)
+//	if err != nil {
+//		return "2", err
+//	}
+//	path := utils.Upload_Audio_Dir + time.Now().Format("200601/20060102/")
+//	path += filename
+//	err = bucket.PutObjectFromFile(path, filepath)
+//	if err != nil {
+//		return "3", err
+//	}
+//	path = utils.Imghost + path
+//	return path, err
+//}
+
+// UploadVideoAliyun 视频上传到阿里云
+//func UploadVideoAliyun(filename, filepath, savePath string) error {
+//	if utils.AccessKeyId == `` {
+//		return errors.New("阿里云信息未配置")
+//	}
+//	defer func() {
+//		os.Remove(filepath)
+//	}()
+//	client, err := oss.New(utils.Endpoint, utils.AccessKeyId, utils.AccessKeySecret)
+//	if err != nil {
+//		return err
+//	}
+//	bucket, err := client.Bucket(utils.Bucketname)
+//	if err != nil {
+//		return err
+//	}
+//	//path := utils.Upload_Audio_Dir + time.Now().Format("200601/20060102/")
+//	//path += filename
+//	err = bucket.PutObjectFromFile(savePath, filepath)
+//	if err != nil {
+//		return err
+//	}
+//	//path = utils.Imghost + path
+//	//return path,err
+//	return err
+//}
+
+// UploadFileToAliyun 上传文件到阿里云
+//func UploadFileToAliyun(filename, filepath, savePath string) error {
+//	if utils.AccessKeyId == `` {
+//		return errors.New("阿里云信息未配置")
+//	}
+//	defer func() {
+//		os.Remove(filepath)
+//	}()
+//	client, err := oss.New(utils.Endpoint, utils.AccessKeyId, utils.AccessKeySecret)
+//	if err != nil {
+//		return err
+//	}
+//	bucket, err := client.Bucket(utils.Bucketname)
+//	if err != nil {
+//		return err
+//	}
+//	//path := utils.Upload_Audio_Dir + time.Now().Format("200601/20060102/")
+//	//path += filename
+//	err = bucket.PutObjectFromFile(savePath, filepath)
+//	if err != nil {
+//		return err
+//	}
+//	//path = utils.Imghost + path
+//	//return path,err
+//	return err
+//}
+
+type STSToken struct {
+	AccessKeyId     string
+	AccessKeySecret string
+	SecurityToken   string
+	ExpiredTime     string
+	RegionId        string
+	Bucketname      string
+	Endpoint        string
+	Imghost         string
+}
+
+// GetOssSTSToken 获取STSToken
+func GetOssSTSToken() (item *STSToken, err error) {
+	defer func() {
+		if err != nil {
+			utils.FileLog.Info(err.Error())
+			go alarm_msg.SendAlarmMsg("获取STSToken失败, ErrMsg: "+err.Error(), 3)
+		}
+	}()
+	item = new(STSToken)
+	// 获取缓存中的Token
+	recent, _ := utils.Rc.RedisString(utils.STSTokenCacheKey)
+	if recent != "" {
+		lastToken := new(STSToken)
+		if e := json.Unmarshal([]byte(recent), &lastToken); e != nil {
+			err = errors.New("GetOssSTSToken lastToken Unmarshal Err: " + e.Error())
+			return
+		}
+		// 未防止正在上传大文件时Token过期, 将判定的过期时间提前10分钟
+		afterTime := time.Now().Local().Add(10 * time.Minute)
+		expired, e := time.ParseInLocation(utils.FormatDateTime, lastToken.ExpiredTime, time.Local)
+		if e != nil {
+			err = errors.New("GetOssSTSToken expiredTime Parse Err: " + e.Error())
+			return
+		}
+		if expired.After(afterTime) {
+			item.AccessKeyId = lastToken.AccessKeyId
+			item.AccessKeySecret = lastToken.AccessKeySecret
+			item.SecurityToken = lastToken.SecurityToken
+			item.ExpiredTime = lastToken.ExpiredTime
+			item.RegionId = utils.RegionId
+			item.Bucketname = utils.Bucketname
+			item.Endpoint = utils.Imghost
+			item.Imghost = utils.Imghost
+			return
+		}
+	}
+	// 已过期则获取新的token
+	newToken, e := NewSTSToken()
+	if e != nil {
+		err = errors.New("GetOssSTSToken NewSTSToken Err: " + e.Error())
+		return
+	}
+	newTokenJson, e := json.Marshal(newToken)
+	if e != nil {
+		err = errors.New("GetOssSTSToken NewToken JSON Err: " + e.Error())
+		return
+	}
+	// 覆盖缓存
+	if e := utils.Rc.Put(utils.STSTokenCacheKey, newTokenJson, time.Hour); e != nil {
+		err = errors.New("GetOssSTSToken SetRedis Err: " + e.Error())
+		return
+	}
+	item = newToken
+	return
+}
+
+// NewSTSToken 获取一个新的STSToken
+func NewSTSToken() (item *STSToken, err error) {
+	defer func() {
+		if err != nil {
+			utils.FileLog.Info(err.Error())
+		}
+	}()
+	item = new(STSToken)
+	client, e := sts.NewClientWithAccessKey("cn-shanghai", utils.RAMAccessKeyId, utils.RAMAccessKeySecret)
+	if e != nil {
+		err = errors.New("NewSTSToken NewClient Err: " + e.Error())
+		return
+	}
+	request := sts.CreateAssumeRoleRequest()
+	request.Scheme = utils.AliStsScheme
+	request.RegionId = utils.RegionId
+	request.RoleArn = utils.RoleArn
+	now := time.Now().Format(utils.FormatDateTimeUnSpace)
+	request.RoleSessionName = utils.RoleSessionName + now
+	request.DurationSeconds = "3600"
+	request.ConnectTimeout = 300 * time.Second
+	request.ReadTimeout = 300 * time.Second
+
+	response, e := client.AssumeRole(request)
+	if e != nil {
+		err = errors.New("NewSTSToken AssumeRole Err: " + e.Error())
+		return
+	}
+	if response != nil {
+		item.AccessKeyId = response.Credentials.AccessKeyId
+		item.AccessKeySecret = response.Credentials.AccessKeySecret
+		item.SecurityToken = response.Credentials.SecurityToken
+		t, _ := time.Parse(time.RFC3339, response.Credentials.Expiration)
+		expiration := t.In(time.Local)
+		item.ExpiredTime = expiration.Format(utils.FormatDateTime)
+		item.RegionId = utils.RegionId
+		item.Bucketname = utils.Bucketname
+		item.Endpoint = utils.Imghost
+		item.Imghost = utils.Imghost
+	}
+	return
+}
+
+// UploadAliyunToDir 上传至hzchart
+//func UploadAliyunToDir(filename, filepath, uploadDir, fileDir string) (string, error) {
+//	if utils.AccessKeyId == `` {
+//		return "0", errors.New("阿里云信息未配置")
+//	}
+//	client, err := oss.New(utils.Endpoint, utils.AccessKeyId, utils.AccessKeySecret)
+//	if err != nil {
+//		return "1", err
+//	}
+//	bucket, err := client.Bucket(utils.Bucketname)
+//	if err != nil {
+//		return "2", err
+//	}
+//	if uploadDir == "" {
+//		uploadDir = utils.UploadDir
+//	}
+//	if fileDir == "" {
+//		fileDir = time.Now().Format("200601/20060102/")
+//	}
+//	path := uploadDir + fileDir
+//	path += filename
+//	err = bucket.PutObjectFromFile(path, filepath)
+//	if err != nil {
+//		return "3", err
+//	}
+//	path = utils.Imghost + path
+//	return path, err
+//}
+
+type AliOss struct{}
+
+// UploadFile 上传文件
+func (m *AliOss) UploadFile(fileName, filePath, savePath string) (string, error) {
+	if utils.AccessKeyId == `` {
+		return "0", errors.New("阿里云信息未配置")
+	}
+	client, err := oss.New(utils.Endpoint, utils.AccessKeyId, utils.AccessKeySecret)
+	if err != nil {
+		return "1", err
+	}
+	bucket, err := client.Bucket(utils.Bucketname)
+	if err != nil {
+		return "2", err
+	}
+
+	path := savePath
+	if savePath == "" {
+		path = utils.UploadDir + time.Now().Format("200601/20060102/") + fileName
+	}
+	err = bucket.PutObjectFromFile(path, filePath)
+	if err != nil {
+		return "3", err
+	}
+	resourceUrl := utils.Imghost + path
+	return resourceUrl, err
+}
+
+func (m *AliOss) GetUploadToken() (token OssToken, err error) {
+	stsToken, e := GetOssSTSToken()
+	if e != nil {
+		err = fmt.Errorf("GetOssSTSToken err: %s", e.Error())
+		return
+	}
+	token.AccessKeyId = stsToken.AccessKeyId
+	token.AccessKeySecret = stsToken.AccessKeySecret
+	token.SecurityToken = stsToken.SecurityToken
+	token.ExpiredTime = stsToken.ExpiredTime
+	token.RegionId = stsToken.RegionId
+	token.Bucketname = stsToken.Bucketname
+	token.Endpoint = stsToken.Endpoint
+	token.Imghost = stsToken.Imghost
+	return
+}

+ 110 - 0
services/qichacha.go

@@ -0,0 +1,110 @@
+package services
+
+import (
+	"encoding/json"
+	"errors"
+	"eta/eta_forum_admin/utils"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+	"strconv"
+	"strings"
+
+	"time"
+)
+
+func GetCompanyInfo() {
+	qccKey := `efd43bfceaa74cf1811079dbcec25c33`
+	qccSecretKey := `2985D128630B085C1BFE8F88630B5DA8`
+	getUrl := `http://api.qichacha.com/ECIV4/Search?key=` + qccKey + `&keyword=913300007125582210`
+
+	timespan := time.Now().Unix()
+	timespanStr := strconv.FormatInt(timespan, 10)
+	token := utils.MD5(qccKey + timespanStr + qccSecretKey)
+	client := &http.Client{}
+	//提交请求
+	reqest, err := http.NewRequest("GET", getUrl, nil)
+	//增加header选项
+	reqest.Header.Add("Token", token)
+	reqest.Header.Add("Timespan", timespanStr)
+
+	if err != nil {
+		panic(err)
+	}
+	//处理返回结果
+	response, _ := client.Do(reqest)
+	defer response.Body.Close()
+
+	body, err := ioutil.ReadAll(response.Body)
+	fmt.Println("body:", string(body))
+	utils.FileLog.Info("%s", string(body))
+}
+
+type QCCResp struct {
+	Paging      Paging   `json:"Paging"`
+	Result      []Result `json:"Result"`
+	Status      string   `json:"Status"`
+	Message     string   `json:"Message"`
+	OrderNumber string   `json:"OrderNumber"`
+}
+type Paging struct {
+	PageSize     int `json:"PageSize"`
+	PageIndex    int `json:"PageIndex"`
+	TotalRecords int `json:"TotalRecords"`
+}
+
+type Result struct {
+	KeyNo      string `json:"KeyNo"`
+	Name       string `json:"Name"`
+	CreditCode string `json:"CreditCode"`
+	StartDate  string `json:"StartDate"`
+	OperName   string `json:"OperName"`
+	Status     string `json:"Status"`
+	No         string `json:"No"`
+}
+
+func QCCFuzzySearch(keyWord string) (results []Result, err error) {
+	qccKey := `49464d73ae1c46e99ad4eff1efe943c1`
+	qccSecretKey := `9DE7E83FFE26789815004C5172503290`
+	getUrl := `http://api.qichacha.com/FuzzySearch/GetList?pageSize=20&key=%s&searchKey=%s `
+	utils.StrFilterNonChinese(&keyWord)
+	finalUrl := fmt.Sprintf(getUrl, qccKey, keyWord)
+	timespan := time.Now().Unix()
+	timespanStr := strconv.FormatInt(timespan, 10)
+	token := utils.MD5(qccKey + timespanStr + qccSecretKey)
+	client := &http.Client{}
+	//提交请求
+	reqest, err := http.NewRequest("GET", finalUrl, nil)
+	if err != nil {
+		utils.FileLog.Info("NewRequest err:" + err.Error())
+		err = errors.New("NewRequest err:" + err.Error())
+		return
+	}
+	//增加header选项
+	reqest.Header.Add("Token", strings.ToUpper(token))
+	reqest.Header.Add("Timespan", timespanStr)
+
+	if err != nil {
+		return
+	}
+	//处理返回结果
+	response, _ := client.Do(reqest)
+	defer response.Body.Close()
+
+	body, err := ioutil.ReadAll(response.Body)
+	var qccResp QCCResp
+	err = json.Unmarshal(body, &qccResp)
+	if err != nil {
+		utils.FileLog.Info("Json Unmarshal err:" + err.Error())
+		err = errors.New("Json Unmarshal err:" + err.Error())
+		return
+	}
+	if qccResp.Status != "200" {
+		utils.FileLog.Info("QCCFuzzySearch err:" + qccResp.Message)
+		err = errors.New("QCCFuzzySearch err:" + qccResp.Message)
+		return
+	} else {
+		results = qccResp.Result
+		return
+	}
+}

+ 2 - 8
services/system.go

@@ -1,12 +1,6 @@
 package services
 
-import (
-	"eta/eta_forum_admin/models/system"
-	"eta/eta_forum_admin/utils"
-	"fmt"
-)
-
-func GetCompanyNameByAdmins(adminIds []int) (adminCompanyMap map[int]string, err error) {
+/*func GetCompanyNameByAdmins(adminIds []int) (adminCompanyMap map[int]string, err error) {
 	// 查询所有机构
 	ob := new(system.EtaBusiness)
 	companyList, er := ob.GetItemsByCondition("", make([]interface{}, 0), []string{}, "")
@@ -41,4 +35,4 @@ func GetCompanyNameByAdmins(adminIds []int) (adminCompanyMap map[int]string, err
 		}
 	}
 	return
-}
+}*/

BIN
static/template/用户导入模版.xlsx


+ 64 - 0
utils/common.go

@@ -1320,3 +1320,67 @@ func ContainsChinese(str string) bool {
 	}
 	return false
 }
+
+func TimeTransferString(format string, t time.Time) string {
+	str := t.Format(format)
+	if t.IsZero() {
+		return ""
+	}
+	return str
+}
+
+// GetDiffDays 计算两个日期相差的天数
+func GetDiffDays(t1, t2 time.Time) int {
+	t1 = time.Date(t1.Year(), t1.Month(), t1.Day(), 0, 0, 0, 0, time.Local)
+	t2 = time.Date(t2.Year(), t2.Month(), t2.Day(), 0, 0, 0, 0, time.Local)
+	return int(t1.Sub(t2).Hours() / 24)
+}
+
+func GetDurationFormatBySecond(sec int) (formatString string) {
+	if sec == 0 {
+		formatString = "0分钟"
+		return
+	} else if sec <= 60 && sec > 0 {
+		formatString = "1分钟"
+		return
+	}
+
+	duration := time.Duration(int64(sec)) * time.Second
+	h := int(duration.Hours())
+	m := int(duration.Minutes()) % 60
+	//s := int(duration.Seconds()) % 60
+	if h > 0 {
+		formatString = fmt.Sprintf("%d小时", h)
+	}
+	if m > 0 {
+		formatString += fmt.Sprintf("%d分钟", m)
+	}
+	//if s > 0 {
+	//	formatString += fmt.Sprintf("%d秒", s)
+	//}
+	return
+}
+
+// 去除非中文字符串
+func StrFilterNonChinese(src *string) {
+	var hzRegexp = regexp.MustCompile("^[\u4e00-\u9fa5]$")
+	strn := ""
+	for _, c := range *src {
+		if hzRegexp.MatchString(string(c)) {
+			strn += string(c)
+		}
+	}
+
+	*src = strn
+}
+
+// GetFiccAdminDepartmentIds 获取ficc管理员的所属部门
+func GetFiccAdminDepartmentIds() (idStr string) {
+	if RunMode == "release" {
+		idStr = `1,2,4,6`
+	} else {
+		idStr = `1,2,6`
+	}
+	return
+}
+

+ 148 - 2
utils/config.go

@@ -28,8 +28,9 @@ var (
 
 // ES索引配置
 var (
-	DATA_INDEX_NAME  string //数据指标库索引
-	CHART_INDEX_NAME string //研究图库索引
+	DATA_INDEX_NAME          string //数据指标库索引
+	CHART_INDEX_NAME         string //研究图库索引
+	CHART_COLLECT_INDEX_NAME string
 )
 
 // 弘则
@@ -59,6 +60,77 @@ var (
 // AlarmMsgUrl 报警服务地址
 var AlarmMsgUrl string
 
+var STATIC_DIR string
+
+// 对象存储客户端
+var (
+	ObjectStorageClient string // 目前有oss minio,默认oss
+	ResourceProxyUrl    string
+	RESOURCE_DIR        string
+)
+
+// 阿里云配置
+var (
+	Bucketname       string
+	Endpoint         string
+	Imghost          string
+	UploadDir        string
+	Upload_Audio_Dir string
+	AccessKeyId      string
+	AccessKeySecret  string
+)
+
+// 阿里云oss前端上传用
+var (
+	AliStsScheme       string
+	RegionId           string
+	RoleArn            string
+	RoleSessionName    string
+	RAMAccessKeyId     string
+	RAMAccessKeySecret string
+	STSTokenCacheKey   string
+)
+
+// MinIo配置
+var (
+	MinIoBucketname       string
+	MinIoEndpoint         string
+	MinIoBackEndpoint     string
+	MinIoImghost          string
+	MinIoUploadDir        string
+	MinIoUpload_Audio_Dir string
+	MinIoAccessKeyId      string
+	MinIoAccessKeySecret  string
+	MinIoUseSSL           string
+	MinIoPort             string
+	MinIoRegion           string
+	MinIoFileDownloadHost string
+)
+
+// S3配置
+var (
+	S3Endpoint        string
+	S3BackEndpoint    string
+	S3BucketName      string
+	S3UploadDir       string
+	S3AccessKeyId     string
+	S3AccessKeySecret string
+	S3Host            string
+	S3Region          string
+	S3ForceStyle      string
+	S3EndpointPort    string
+	S3Protocol        string
+	S3DisableSSL      string
+	S3OpenAcl         string
+)
+
+// eta_forum_hub ETA社区桥接服务地址
+var (
+	ETA_FORUM_HUB_URL     string
+	ETA_FORUM_HUB_NAME_EN string
+	ETA_FORUM_HUB_MD5_KEY string
+)
+
 func init() {
 	tmpRunMode, err := web.AppConfig.String("run_mode")
 	if err != nil {
@@ -140,6 +212,80 @@ func init() {
 	{
 		DATA_INDEX_NAME = config["data_index_name"]
 		CHART_INDEX_NAME = config["chart_index_name"]
+		CHART_COLLECT_INDEX_NAME = config["chart_collect_index_name"]
+	}
+
+	// 静态文件目录
+	STATIC_DIR = config["static_dir"]
+	if STATIC_DIR == "" {
+		// 静态文件目录
+		STATIC_DIR = "static"
+	}
+
+	{
+		// 对象存储客户端
+		ObjectStorageClient = config["object_storage_client"]
+
+		// OSS相关
+		{
+			Endpoint = config["endpoint"]
+			Bucketname = config["bucket_name"]
+			Imghost = config["img_host"]
+			UploadDir = config["upload_dir"]
+			Upload_Audio_Dir = config["upload_audio_dir"]
+			AccessKeyId = config["access_key_id"]
+			AccessKeySecret = config["access_key_secret"]
+		}
+
+		// OSS相关(前端使用)
+		{
+			AliStsScheme = config["ali_sts_scheme"]
+			RegionId = config["region_id"]
+			RoleArn = config["role_arn"]
+			RoleSessionName = config["role_session_name"]
+			RAMAccessKeyId = config["ram_access_key_id"]
+			RAMAccessKeySecret = config["ram_access_key_secret"]
+			STSTokenCacheKey = config["sts_token_cache_key"]
+		}
+
+		// MinIo相关
+		{
+			MinIoEndpoint = config["minio_endpoint"]
+			MinIoBackEndpoint = config["minio_back_endpoint"]
+			MinIoBucketname = config["minio_bucket_name"]
+			MinIoImghost = config["minio_img_host"]
+			MinIoUploadDir = config["minio_upload_dir"]
+			MinIoUpload_Audio_Dir = config["minio_upload_audio_dir"]
+			MinIoAccessKeyId = config["minio_access_key_id"]
+			MinIoAccessKeySecret = config["minio_access_key_secret"]
+			MinIoUseSSL = config["minio_use_ssl"]
+			MinIoPort = config["minio_port"]
+			MinIoRegion = config["minio_region"]
+			MinIoFileDownloadHost = config["minio_file_download_host"]
+		}
+
+		// S3-OSS相关
+		{
+			S3Endpoint = config["s3_endpoint"]
+			S3BackEndpoint = config["s3_back_endpoint"]
+			S3BucketName = config["s3_bucket_name"]
+			S3Host = config["s3_host"]
+			S3AccessKeyId = config["s3_access_key_id"]
+			S3AccessKeySecret = config["s3_access_key_secret"]
+			S3UploadDir = config["s3_upload_dir"]
+			S3Region = config["s3_region"]
+			S3ForceStyle = config["s3_force_style"]
+			S3EndpointPort = config["s3_endpoint_port"]
+			S3Protocol = config["s3_protocol"]
+			S3DisableSSL = config["s3_disable_ssl"]
+			S3OpenAcl = config["s3_open_acl"]
+		}
+		// eta_forum_hub ETA社区桥接服务地址
+		{
+			ETA_FORUM_HUB_URL = config["eta_forum_hub_url"]
+			ETA_FORUM_HUB_NAME_EN = config["eta_forum_hub_name_en"]
+			ETA_FORUM_HUB_MD5_KEY = config["eta_forum_hub_md5_key"]
+		}
 	}
 }
 

+ 27 - 1
utils/constants.go

@@ -1,6 +1,9 @@
 package utils
 
-import "time"
+import (
+	"io/fs"
+	"time"
+)
 
 // 常量定义
 const (
@@ -346,3 +349,26 @@ var DataSourceEnMap = map[int]string{
 	DATA_SOURCE_MYSTEEL_CHEMICAL: "Horizon Insights",
 	DATA_SOURCE_FUBAO:            "FuBao",
 }
+
+// BusinessCodeSalt 商家编码盐值
+const BusinessCodeSalt = "dr7WY0OZgGR7upw1"
+
+//const CrmEtaAuthorization = "NIi1RbEmH0C2rksXtPGDPBBgRgTZY87Q"
+
+const HzCompanyName = "弘则研究"
+const HzBusinessCodeRelease = "E2023080900" // 生产环境
+
+// DIR_MOD 目录创建权限
+const DIR_MOD fs.FileMode = 0766 // Unix permission bits
+const (
+	STORAGESOURCE_OSS_NAME   = "oss"
+	STORAGESOURCE_MINIO_NAME = "minio"
+	STORAGESOURCE_S3_NAME    = "s3"
+)
+
+// 客户类型
+const (
+	COMPANY_CLASSIFY_FICC    = "ficc"
+	COMPANY_CLASSIFY_RAI     = "权益"
+	COMPANY_CLASSIFY_PARTNER = "合作伙伴"
+)

+ 16 - 0
utils/des3.go

@@ -185,3 +185,19 @@ func DecryptDESECB(d string, key []byte) ([]byte, error) {
 	out = PKCS5UnPadding(out)
 	return out, nil
 }
+
+func DesBase64DecryptNoKey(crypted []byte) []byte {
+	result, _ := base64.StdEncoding.DecodeString(string(crypted))
+	remain := len(result) % 8
+	if remain > 0 {
+		mod := 8 - remain
+		for i := 0; i < mod; i++ {
+			result = append(result, 0)
+		}
+	}
+	origData, err := TripleDesDecrypt(result, []byte(DesKey))
+	if err != nil {
+		panic(any(err))
+	}
+	return origData
+}