Sfoglia il codice sorgente

同步crm中的商家管理功能

xyxie 3 mesi fa
parent
commit
09b415f22d
44 ha cambiato i file con 10570 aggiunte e 8 eliminazioni
  1. 1514 0
      controllers/eta_business/eta_business.go
  2. 764 0
      controllers/eta_business/eta_business_menu.go
  3. 1957 0
      controllers/eta_trial.go
  4. 539 0
      controllers/help_doc/classify.go
  5. 448 0
      controllers/help_doc/doc.go
  6. 72 0
      controllers/sys_menu.go
  7. 334 0
      controllers/sys_role.go
  8. 1 0
      go.mod
  9. 2 0
      go.sum
  10. 180 0
      models/company/company_approval_message.go
  11. 43 0
      models/db.go
  12. 344 0
      models/eta_business/eta_business.go
  13. 118 0
      models/eta_business/eta_business_config_relate.go
  14. 205 0
      models/eta_business/eta_business_contract.go
  15. 227 0
      models/eta_business/eta_business_menu.go
  16. 117 0
      models/eta_business/eta_business_menu_icon.go
  17. 133 0
      models/eta_business/eta_business_menu_relate.go
  18. 138 0
      models/eta_business/eta_business_operation_record.go
  19. 177 0
      models/eta_trial/eta_trial.go
  20. 226 0
      models/eta_trial/eta_trial_approval.go
  21. 71 0
      models/eta_trial/eta_trial_manual.go
  22. 155 0
      models/eta_trial/eta_trial_questionnaire.go
  23. 135 0
      models/eta_trial/eta_trial_questionnaire_record.go
  24. 33 0
      models/eta_trial/eta_trial_record.go
  25. 84 0
      models/eta_trial/sys_user.go
  26. 260 0
      models/help_doc/classify.go
  27. 198 0
      models/help_doc/help_doc.go
  28. 18 0
      models/system/admin.go
  29. 82 0
      models/system/crm_config.go
  30. 214 0
      models/system/sys_menu.go
  31. 87 0
      models/system/sys_menu_button.go
  32. 269 0
      models/system/sys_role.go
  33. 256 0
      models/system/sys_role_admin.go
  34. 504 0
      routers/commentsRouter.go
  35. 25 0
      routers/router.go
  36. 130 0
      services/crm_eta.go
  37. 122 0
      services/eta_business/eta_business.go
  38. 46 0
      services/eta_business/eta_business_menu.go
  39. 224 0
      services/eta_trial.go
  40. 32 0
      services/help_doc_classify.go
  41. 40 0
      utils/common.go
  42. 25 8
      utils/config.go
  43. 5 0
      utils/constants.go
  44. 16 0
      utils/des3.go

+ 1514 - 0
controllers/eta_business/eta_business.go

@@ -0,0 +1,1514 @@
+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"
+	etaBusinessService "eta/eta_forum_admin/services/eta_business"
+	"eta/eta_forum_admin/utils"
+	"fmt"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"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
+	}
+
+	// 操作日志
+	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 = "操作成功"
+}
+
+// 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", "UserMax", "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 = "操作成功"
+}

+ 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 = "设置成功"
+}

+ 1957 - 0
controllers/eta_trial.go

@@ -0,0 +1,1957 @@
+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/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{}
+
+	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, err := eta_trial.GetSysAdminCountByMobile(v.Mobile, 0)
+		if err != nil {
+			br.Msg = "判断手机号是否存在失败"
+			br.ErrMsg = "判断手机号是否存在失败,Err:" + err.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_trial.UpdateAdminEnable(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
+		}
+
+		//新增至试用平台的Admin
+
+		adminItem, err := eta_trial.GetSysUserByRoleAndDepartment("试用", "ETA试用客户")
+		if err != nil {
+			br.Msg = "获取用户信息失败"
+			br.ErrMsg = "获取用户信息失败,Err:" + err.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, err := eta_trial.AddAdmin(admin)
+		if err != nil {
+			br.Msg = "新增失败"
+			br.ErrMsg = "新增失败,AddAdmin Err:" + err.Error()
+			return
+		}
+
+		// 新增试用客户手工权限
+		go func() {
+			//_ = services.CreateTrialUserManualAuth(newId, admin.RealName)
+			_ = services.EtaTrialManualUserAddAuth(newId, admin.RealName)
+		}()
+	}
+
+	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 + `%') `
+	}
+
+	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
+}

+ 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 = "删除成功"
+}

+ 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
+}

+ 1 - 0
go.mod

@@ -9,6 +9,7 @@ require (
 	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/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

+ 2 - 0
go.sum

@@ -138,6 +138,8 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
 github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 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=

+ 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
+}

+ 43 - 0
models/db.go

@@ -1,6 +1,9 @@
 package models
 
 import (
+	"eta/eta_forum_admin/models/company"
+	"eta/eta_forum_admin/models/eta_business"
+	"eta/eta_forum_admin/models/eta_trial"
 	"eta/eta_forum_admin/models/system"
 	"eta/eta_forum_admin/utils"
 	"github.com/beego/beego/v2/client/orm"
@@ -16,12 +19,19 @@ func init() {
 	db, _ := orm.GetDB("default")
 	db.SetConnMaxLifetime(10 * time.Minute)
 
+	_ = orm.RegisterDataBase("weekly_trial", "mysql", utils.MYSQL_URL_WEEKLY_TRIAL)
+	orm.SetMaxIdleConns("weekly_trial", 50)
+	orm.SetMaxOpenConns("weekly_trial", 100)
+
 	orm.Debug = true
 	orm.DebugLog = orm.NewLog(utils.Binlog)
 
 	initChart()
 	initEdbData()
 	initUser()
+	initETATrial()
+	initEtaBusiness()
+	initSystem()
 }
 
 // initChart 图表 数据表
@@ -54,3 +64,36 @@ 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表
+	)
+}
+
+// initSystem 系统表 数据表
+func initSystem() {
+	orm.RegisterModel(
+		new(system.SysRole),
+		new(system.SysMenuButton),
+		new(system.SysRoleAdmin), //管理员账号和角色映射表
+		new(system.SysRoleMenu),
+	)
+}

+ 344 - 0
models/eta_business/eta_business.go

@@ -0,0 +1,344 @@
+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) 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:"分页数据"`
+}
+
+// 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"`
+}

+ 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:"创建时间"`
+}

+ 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
+}

+ 84 - 0
models/eta_trial/sys_user.go

@@ -0,0 +1,84 @@
+package eta_trial
+
+import (
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+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
+}

+ 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:"修改时间"`
+}

+ 18 - 0
models/system/admin.go

@@ -189,3 +189,21 @@ 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
+}

+ 82 - 0
models/system/crm_config.go

@@ -0,0 +1,82 @@
+package system
+
+import (
+	"encoding/json"
+	"errors"
+	"eta/eta_forum_admin/utils"
+	"github.com/beego/beego/v2/client/orm"
+)
+
+const (
+	ConfAreaCodeListKey = "area_code_list" // 手机号区号列表
+)
+
+type CrmConfig struct {
+	ConfigCode  string `description:"详情Code"`
+	ConfigValue 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
+}
+
+// 修改
+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
+}

+ 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 a.type = 0
+				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"`
+}

+ 504 - 0
routers/commentsRouter.go

@@ -7,6 +7,285 @@ 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: "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: "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/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",
@@ -142,6 +421,177 @@ func init() {
             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",
@@ -160,4 +610,58 @@ func init() {
             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})
+
 }

+ 25 - 0
routers/router.go

@@ -9,6 +9,8 @@ package routers
 
 import (
 	"eta/eta_forum_admin/controllers"
+	"eta/eta_forum_admin/controllers/eta_business"
+	"eta/eta_forum_admin/controllers/help_doc"
 	"github.com/beego/beego/v2/server/web"
 )
 
@@ -35,6 +37,29 @@ 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{},
+			),
+		),
+		web.NSNamespace("/eta_trial",
+			web.NSInclude(
+				&controllers.ETATrialController{},
+			),
+		),
+		web.NSNamespace("/help_doc",
+			web.NSInclude(
+				&help_doc.HelpDocClassifyController{},
+				&help_doc.HelpDocController{},
+			),
+		),
 	)
 	web.AddNamespace(ns)
 }

+ 130 - 0
services/crm_eta.go

@@ -0,0 +1,130 @@
+package services
+
+import (
+	"encoding/json"
+	"eta/eta_forum_admin/services/alarm_msg"
+	"eta/eta_forum_admin/utils"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+	"strings"
+)
+
+const (
+	LoginAuthCodeSource = 1
+)
+
+// MiddleServerResultData 中间服务响应体
+type MiddleServerResultData struct {
+	Code   int         `json:"code" description:"状态码"`
+	Msg    string      `json:"msg" description:"提示信息"`
+	Data   interface{} `json:"data" description:"返回数据"`
+	ErrMsg string      `json:"-" description:"错误信息,不用返回给前端,只是做日志记录"`
+}
+
+// GetLoginAuthCodeReq 获取登录编码请求体
+type GetLoginAuthCodeReq struct {
+	AdminName string `json:"admin_name" description:"用户名"`
+	Source    int    `json:"source" description:"来源: 1-CRM; 2-ETA"`
+}
+
+// GetEtaTokenReq 获取Token请求体
+type GetEtaTokenReq struct {
+	AuthCode string `json:"auth_code"`
+}
+
+// GetEtaTokenDataResp 获取Token信息响应体
+type GetEtaTokenDataResp struct {
+	Code int             `json:"code" description:"状态码"`
+	Msg  string          `json:"msg" description:"提示信息"`
+	Data GetEtaTokenData `json:"data" description:"返回数据"`
+}
+
+// GetEtaTokenData Token数据
+type GetEtaTokenData struct {
+	Authorization   string `description:"Auth Token"`
+	AdminName       string `description:"系统用户名称"`
+	RealName        string `description:"系统用户姓名"`
+	RoleName        string `description:"角色名称"`
+	RoleTypeCode    string `description:"角色类型编码"`
+	SysRoleTypeCode string `description:"角色类型编码"`
+	AdminId         int    `description:"系统用户id"`
+	ProductName     string `description:"产品名称:admin,ficc,权益"`
+	Authority       int    `description:"管理权限,0:无,1:部门负责人,2:小组负责人,或者ficc销售主管,4:ficc销售组长"`
+}
+
+// EtaTrialManualUserAddAuthReq 体验版用户添加手工权限
+type EtaTrialManualUserAddAuthReq struct {
+	AdminId   int    `json:"admin_id" description:"用户ID"`
+	AdminName string `json:"admin_name" description:"用户名"`
+}
+
+// EtaTrialManualUserAddAuth CRM_ETA服务-体验版用户添加手工权限
+func EtaTrialManualUserAddAuth(adminId int, adminName string) (err error) {
+	// 体验版无测试环境
+	//if utils.RunMode != "release" {
+	//	return
+	//}
+	defer func() {
+		if err != nil {
+			alarm_msg.SendAlarmMsg(fmt.Sprintf("EtaTrialManualUserAddAuth, 新增试用客户手工录入权限失败, ErrMsg: %s", err.Error()), 3)
+		}
+	}()
+
+	url := fmt.Sprint(utils.CrmEtaServerUrl, "/api/eta_trial/manual_user/add_auth")
+	param := EtaTrialManualUserAddAuthReq{
+		AdminId:   adminId,
+		AdminName: adminName,
+	}
+	data, e := json.Marshal(param)
+	if e != nil {
+		err = fmt.Errorf("data json marshal err: %s", e.Error())
+		return
+	}
+
+	body := ioutil.NopCloser(strings.NewReader(string(data)))
+	client := &http.Client{}
+	req, e := http.NewRequest("POST", url, body)
+	if e != nil {
+		err = fmt.Errorf("http create request err: %s", e.Error())
+		return
+	}
+
+	contentType := "application/json;charset=utf-8"
+	req.Header.Set("Content-Type", contentType)
+	req.Header.Set("Authorization", utils.CrmEtaAuthorization)
+	resp, e := client.Do(req)
+	if e != nil {
+		err = fmt.Errorf("http client do err: %s", e.Error())
+		return
+	}
+	defer func() {
+		_ = resp.Body.Close()
+	}()
+	b, e := ioutil.ReadAll(resp.Body)
+	if e != nil {
+		err = fmt.Errorf("resp body read err: %s", e.Error())
+		return
+	}
+	if len(b) == 0 {
+		err = fmt.Errorf("resp body is empty")
+		return
+	}
+	// 生产环境解密, 注意有个坑前后的双引号
+	if utils.RunMode == "release" {
+		str := string(b)
+		str = strings.Trim(str, `"`)
+		b = utils.DesBase64DecryptNoKey([]byte(str))
+	}
+
+	result := new(MiddleServerResultData)
+	if e = json.Unmarshal(b, &result); e != nil {
+		err = fmt.Errorf("result unmarshal err: %s\nresult: %s", e.Error(), string(b))
+		return
+	}
+	if result.Code != 200 {
+		err = fmt.Errorf("result: %s", string(b))
+		return
+	}
+	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
+}

+ 224 - 0
services/eta_trial.go

@@ -0,0 +1,224 @@
+package services
+
+import (
+	"eta/eta_forum_admin/models/company"
+	"eta/eta_forum_admin/models/eta_trial"
+	"eta/eta_forum_admin/services/alarm_msg"
+	"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_trial.UpdateAdminEnable(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)
+			_ = EtaTrialManualUserAddAuth(newId, admin.RealName)
+		}()
+	}
+
+	// 推送消息给销售
+	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
+}

+ 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
+	}
+}

+ 40 - 0
utils/common.go

@@ -1306,3 +1306,43 @@ func GetDaysBetween2Date(format, date1Str, date2Str string) (int, error) {
 	//计算相差天数
 	return int(date1.Sub(date2).Hours() / 24), nil
 }
+
+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
+}

+ 25 - 8
utils/config.go

@@ -9,14 +9,15 @@ import (
 )
 
 var (
-	RunMode       string //运行模式
-	APP_NAME_CN   string
-	MYSQL_URL     string        //数据库连接
-	Re            error         //redis错误
-	Rc            RedisClient   //redis缓存
-	MgoUrlData    string        // mongodb数据库连接配置
-	MgoDataCli    *mongo.Client // mongodb客户端连接
-	MgoDataDbName string        // mongodb指标数据的库名
+	RunMode                string //运行模式
+	APP_NAME_CN            string
+	MYSQL_URL              string        //数据库连接
+	MYSQL_URL_WEEKLY_TRIAL string        //eta试用数据库
+	Re                     error         //redis错误
+	Rc                     RedisClient   //redis缓存
+	MgoUrlData             string        // mongodb数据库连接配置
+	MgoDataCli             *mongo.Client // mongodb客户端连接
+	MgoDataDbName          string        // mongodb指标数据的库名
 )
 
 // ES配置
@@ -59,6 +60,10 @@ var (
 // AlarmMsgUrl 报警服务地址
 var AlarmMsgUrl string
 
+// CrmEtaServerUrl CRM-ETA服务地址
+var CrmEtaServerUrl string
+var CrmEtaAuthorization string
+
 func init() {
 	tmpRunMode, err := web.AppConfig.String("run_mode")
 	if err != nil {
@@ -94,6 +99,7 @@ func init() {
 	}
 	APP_NAME_CN = appNameCn
 	MYSQL_URL = config["mysql_url"]
+	MYSQL_URL_WEEKLY_TRIAL = config["mysql_url_weekly_trial"]
 	// mongodb数据库连接配置
 	MgoUrlData = config["mgo_url_data"]
 	//日志配置
@@ -141,6 +147,17 @@ func init() {
 		DATA_INDEX_NAME = config["data_index_name"]
 		CHART_INDEX_NAME = config["chart_index_name"]
 	}
+
+	// CRM-ETA服务地址
+	CrmEtaServerUrl = "http://127.0.0.1:8708"
+	if config["crm_eta_server_url"] != "" {
+		CrmEtaServerUrl = config["crm_eta_server_url"]
+	}
+	CrmEtaAuthorization = "NIi1RbEmH0C2rksXtPGDPBBgRgTZY87Q"
+	if config["crm_eta_authorization"] != "" {
+		CrmEtaAuthorization = config["crm_eta_authorization"]
+	}
+
 }
 
 //修改接口文档

+ 5 - 0
utils/constants.go

@@ -345,3 +345,8 @@ var DataSourceEnMap = map[int]string{
 	DATA_SOURCE_MYSTEEL_CHEMICAL: "Horizon Insights",
 	DATA_SOURCE_FUBAO:            "FuBao",
 }
+
+// BusinessCodeSalt 商家编码盐值
+const BusinessCodeSalt = "dr7WY0OZgGR7upw1"
+
+//const CrmEtaAuthorization = "NIi1RbEmH0C2rksXtPGDPBBgRgTZY87Q"

+ 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
+}