Эх сурвалжийг харах

Merge branch 'crm_need_963' of http://8.136.199.33:3000/hongze/hongze_task into debug

zhangchuanxing 3 долоо хоног өмнө
parent
commit
5afeb07bac

+ 13 - 0
models/admin.go

@@ -2,6 +2,7 @@ package models
 
 import (
 	"github.com/beego/beego/v2/client/orm"
+	"hongze/hongze_task/models/system"
 	"hongze/hongze_task/utils"
 )
 
@@ -33,6 +34,18 @@ func GetAdminByAdminIds(adminIds []int) (items []*Admin, err error) {
 	return
 }
 
+func GetAdminByAdminMobiles(mobiles []string) (items []*system.AdminItem, err error) {
+	sql := `SELECT * FROM admin WHERE mobile in (` + utils.GetOrmInReplace(len(mobiles)) + `) `
+	_, err = orm.NewOrm().Raw(sql, mobiles).QueryRows(&items)
+	return
+}
+
+func GetAdminByAdminAllList() (items []*system.AdminItem, err error) {
+	sql := `SELECT * FROM admin WHERE mobile != '' `
+	_, err = orm.NewOrm().Raw(sql).QueryRows(&items)
+	return
+}
+
 type AdminView struct {
 	AdminId      int
 	AdminName    string `description:"系统用户名称"`

+ 7 - 0
models/cygx/activity_ask_email.go

@@ -10,6 +10,13 @@ type AskEmailRep struct {
 	AdminId             int
 }
 
+func GetAskEmailList() (item []*AskEmailRep, err error) {
+	o := orm.NewOrmUsingDB("hz_cygx")
+	sql := `SELECT * FROM cygx_activity_ask_email WHERE    chart_permission_name != ''  ORDER BY sort  DESC`
+	_, err = o.Raw(sql).QueryRows(&item)
+	return
+}
+
 func GetAskEmail() (item []*AskEmailRep, err error) {
 	o := orm.NewOrmUsingDB("hz_cygx")
 	sql := `SELECT * FROM cygx_activity_ask_email`

+ 14 - 0
models/db.go

@@ -134,6 +134,9 @@ func init() {
 
 	// ETA商家配置表
 	initEtaBusinessConf()
+
+	// 路演记录 数据表
+	initRoadShow()
 }
 
 // initCompany 注册客户信息 数据表
@@ -276,3 +279,14 @@ func initEnglishReport() {
 		new(EnglishReport),
 	)
 }
+
+// 路演记录 数据表
+func initRoadShow() {
+	//注册对象
+	orm.RegisterModel(
+		new(roadshow.RsCalendar),           //路演主表
+		new(roadshow.RsCalendarResearcher), //路演研究员信息表
+		new(roadshow.RsMatters),            //公开会议表
+		new(roadshow.RsCalendarRelation),   //路演关系表(与上海的路演日历关系)
+	)
+}

+ 99 - 0
models/roadshow/calendar.go

@@ -2,6 +2,7 @@ package roadshow
 
 import (
 	"github.com/beego/beego/v2/client/orm"
+	"hongze/hongze_task/utils"
 	"time"
 )
 
@@ -23,6 +24,7 @@ type RsCalendarResearcher struct {
 	RefuseTime             time.Time `description:"拒绝时间"`
 	DeleteReason           string    `description:"删除理由"`
 	UnionCode              string    `description:"公开会议联合编码"`
+	IsSynced               int       `description:"是否与上海同步 0:未同步 1:已同步"`
 }
 
 func GetRsCalendarResearcher(endDate, endTime string) (list []*RsCalendarResearcher, err error) {
@@ -101,6 +103,17 @@ func GetRsCalendarById(rsCalendarId int) (item *RsCalendar, err error) {
 	return
 }
 
+func GetRsCalendarByIds(rsCalendarIds []int) (items []*RsCalendar, err error) {
+	lenArr := len(rsCalendarIds)
+	if lenArr == 0 {
+		return
+	}
+	o := orm.NewOrm()
+	sql := `SELECT * FROM rs_calendar WHERE rs_calendar_id  IN  (` + utils.GetOrmInReplace(lenArr) + `) `
+	_, err = o.Raw(sql, rsCalendarIds).QueryRows(&items)
+	return
+}
+
 func GetRsCalendarResearcherById(rsCalendarId int) (item *RsCalendarResearcher, err error) {
 	sql := `SELECT * FROM rs_calendar_researcher WHERE rs_calendar_researcher_id=? `
 	o := orm.NewOrm()
@@ -108,6 +121,92 @@ func GetRsCalendarResearcherById(rsCalendarId int) (item *RsCalendarResearcher,
 	return
 }
 
+// 根据会议ID,研究员ID获取会议详情
+func GetRsCalendarResearcherByRsCalendarIdAndResearcherId(rsCalendarId, researcherId int) (item *RsCalendarResearcher, err error) {
+	sql := `SELECT * FROM rs_calendar_researcher WHERE rs_calendar_id=? AND researcher_id = ? `
+	o := orm.NewOrm()
+	err = o.Raw(sql, rsCalendarId, researcherId).QueryRow(&item)
+	return
+}
+
+func GetRsCalendarResearcherByIds(rsCalendarResearcherId []int) (items []*RsCalendarResearcher, err error) {
+	lenArr := len(rsCalendarResearcherId)
+	if lenArr == 0 {
+		return
+	}
+	sql := `SELECT * FROM rs_calendar_researcher WHERE rs_calendar_researcher_id  IN  (` + utils.GetOrmInReplace(lenArr) + `) `
+	o := orm.NewOrm()
+	_, err = o.Raw(sql, rsCalendarResearcherId).QueryRows(&items)
+	return
+}
+
+type ResearcherGroup struct {
+	GroupId        int    `description:"分组id"`
+	GroupName      string `description:"分组名称"`
+	AdminId        int    `description:"研究员id"`
+	RealName       string `description:"研究员名称"`
+	RoleTypeCode   string `description:"角色编码"`
+	Mobile         string `description:"手机号"`
+	ResearcherList []*ResearcherGroup
+}
+
+func GetResearcher() (list []*ResearcherGroup, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM admin AS a
+			WHERE a.role_type_code IN('researcher','rai_researcher','ficc_researcher','ficc_admin')
+			AND a.enabled=1 AND a.real_name<>'于菲' `
+	_, err = o.Raw(sql).QueryRows(&list)
+	return
+}
+
+// GetRsCalendarResearcherListByRsCalendarIds 根据路演id获取路演研究员列表
+func GetRsCalendarResearcherListByRsCalendarIds(rsCalendarIds []int) (items []*RsCalendarResearcher, err error) {
+	lenArr := len(rsCalendarIds)
+	if lenArr == 0 {
+		return
+	}
+	o := orm.NewOrm()
+	sql := `SELECT * FROM rs_calendar_researcher WHERE rs_calendar_id  IN  (` + utils.GetOrmInReplace(lenArr) + `) `
+	_, err = o.Raw(sql, rsCalendarIds).QueryRows(&items)
+	return
+}
+
+// 更新
+func UpdateCalendarResearcher(where, updateParams map[string]interface{}) error {
+	o := orm.NewOrm()
+	ptrStructOrTableName := "rs_calendar_researcher"
+	qs := o.QueryTable(ptrStructOrTableName)
+	for expr, exprV := range where {
+		qs = qs.Filter(expr, exprV)
+	}
+	_, err := qs.Update(updateParams)
+	return err
+}
+
+// 软删除
+func ModifyRsCalendarResearcherStatusDel(rsCalendarResearcherIds []int) (err error) {
+	lenArr := len(rsCalendarResearcherIds)
+	if lenArr == 0 {
+		return
+	}
+	sql := ` UPDATE rs_calendar_researcher SET status=4,modify_time=NOW() WHERE rs_calendar_researcher_id IN (` + utils.GetOrmInReplace(lenArr) + `) `
+	o := orm.NewOrm()
+	_, err = o.Raw(sql, rsCalendarResearcherIds).Exec()
+	return
+}
+
+// 更新活动信息
+func UpdateRsCalendar(where, updateParams map[string]interface{}) error {
+	o := orm.NewOrm()
+	ptrStructOrTableName := "rs_calendar"
+	qs := o.QueryTable(ptrStructOrTableName)
+	for expr, exprV := range where {
+		qs = qs.Filter(expr, exprV)
+	}
+	_, err := qs.Update(updateParams)
+	return err
+}
+
 type CalendarTesearcherQuestionTips struct {
 	CompanyName            string
 	OpenId                 string

+ 331 - 0
models/roadshow/rs_calendar_relation.go

@@ -0,0 +1,331 @@
+package roadshow
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"hongze/hongze_task/models/system"
+	"hongze/hongze_task/utils"
+	"strconv"
+	"time"
+)
+
+// RsCalendarRelation 自系统路演与第三方路演关系表
+type RsCalendarRelation struct {
+	RelationId       int       `orm:"column(relation_id);pk" description:"关系id"`
+	CalendarType     int8      `description:"日历类型;1:路演;2:事项"`
+	SelfCalendarId   int       `description:"系统内部的日历id,可以是研究员与路演活动的关系id,也可以是事项id"`
+	ThirdCalendarId  int       `description:"第三方路演id"`
+	UserId           int       `description:"关系id"`
+	UserPhone        string    `description:"创建人手机号"`
+	UserName         string    `description:"创建人昵称"`
+	ProjectName      string    `description:"活动名称"`
+	ProjectId        int       `description:"活动id"`
+	CustomerId       int       `description:"客户id"`
+	CustomerName     string    `description:"客户名称"`
+	CustomerSocial   string    `description:"客户社会信用码"`
+	ProjectType      int       `description:"活动类型:1=沙龙,2=路演,3=专家需求,4=研究需求,5=电话,6=面谈,7=专题需求,8=线下沙龙,9=公司调研"`
+	ProjectFormType  int       `description:"服务形式:1=沙龙,2=路演,3=专家需求,4=研究需求,5=电话,6=面谈,7=专题需求"`
+	Room             int       `description:"会议室id"`
+	StartTime        int       `description:"开始时间戳"`
+	EndTime          int       `description:"结束时间戳"`
+	Content          string    `description:"活动内容"`
+	FeedExpert       string    `description:"邀请的专家"`
+	Title            string    `description:"日历显示的标题"`
+	ResearcherMobile string    `description:"研究员+协同人员手机号(多个使用逗号拼接)"`
+	ModifyTime       time.Time `description:"更新时间"`
+	CreateTime       time.Time `description:"关系建立时间"`
+}
+
+// RsCalendarResearcherRelationInfo
+type RsCalendarResearcherRelationInfo struct {
+	RsCalendarResearcherId int    `orm:"column(rs_calendar_researcher_id);pk"`
+	CalendarType           int8   `description:"日历类型;1:路演;2:事项"`
+	ThirdCalendarId        int    `description:"第三方路演id"`
+	RsCalendarId           int    `description:"日历活动id"`
+	ResearcherId           int    `description:"研究员id"`
+	ResearcherName         string `description:"研究员名称"`
+	StartDate              string `description:"开始日期"`
+	EndDate                string `description:"结束日期"`
+	StartTime              string `description:"开始时间"`
+	EndTime                string `description:"结束时间"`
+	StartWeek              string `description:"开始日期对应周"`
+	EndWeek                string `description:"结束日期对应周"`
+	CreateTime             time.Time
+	ModifyTime             time.Time
+	Status                 int       `description:"状态:1:待接受,2:已接受,3:已拒绝,4:已删除,5:已撤回,6:已结束"`
+	RefuseReason           string    `description:"拒绝理由"`
+	RefuseTime             time.Time `description:"拒绝时间"`
+	DeleteReason           string    `description:"删除理由"`
+	DeleteTime             time.Time `description:"删除时间"`
+	ApproveTime            time.Time `description:"接受时间"`
+	IsSynced               int       `description:"是否与上海同步 0:未同步 1:已同步"`
+	ResearcherSort         int       `description:"研究员新增排序"`
+}
+
+// GetRsCalendarRelationListByThirdIds 根据第三方id集合获取所有的关系列表
+func GetRsCalendarRelationListByThirdIds(thirdCalendarIds []int) (items []*RsCalendarRelation, err error) {
+	if len(thirdCalendarIds) <= 0 {
+		return
+	}
+	thirdCalendarIdStr := utils.Implode(thirdCalendarIds)
+	o := orm.NewOrm()
+	sql := `SELECT * FROM rs_calendar_relation WHERE third_calendar_id in (` + thirdCalendarIdStr + `) `
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}
+
+// GetRsCalendarResearcherInfoIByResearcherIdAndDate 根据研究员和开始/结束日期获取上海的活动路演
+func GetRsCalendarResearcherInfoIByResearcherIdAndDate(startDate, endDate string) (items []*RsCalendarResearcherRelationInfo, err error) {
+	o := orm.NewOrm()
+	//杭州创建的路演活动,如果上海被删除了,那么也要同步删除杭州的(所以相对于上面的逻辑,下面移除了来源的where条件)
+	sql := `SELECT a.*,c.third_calendar_id,c.calendar_type FROM rs_calendar_researcher a 
+join rs_calendar b on a.rs_calendar_id=b.rs_calendar_id
+join rs_calendar_relation c on a.rs_calendar_researcher_id=c.self_calendar_id
+WHERE a.status=2 and c.calendar_type=1  and start_date>=? and end_date <= ? `
+	_, err = o.Raw(sql, startDate, endDate).QueryRows(&items)
+	return
+}
+
+// UpdateSyncRsCalendarRelation 同步自系统路演与第三方路演关系
+func UpdateSyncRsCalendarRelation(thirdUserCalendar UserCalendar, rsCalendar *RsCalendar, rsCalendarRelation *RsCalendarRelation, updateRsCalendarResearcherList []*RsCalendarResearcher, delResearcherIdList []int, addResearcherList []*system.AdminItem) (err error) {
+	currentStartTimer := time.Unix(int64(thirdUserCalendar.StartTime), 0)
+	currentEndTimer := time.Unix(int64(thirdUserCalendar.EndTime), 0)
+
+	//新增研究员
+	//删除研究员
+	//更新研究员
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	// 路演活动表修改
+	rsCalendar.Title = thirdUserCalendar.Title
+	rsCalendar.ModifyTime = time.Now()
+	_, err = to.Update(rsCalendar, "Title", "ModifyTime")
+	if err != nil {
+		return
+	}
+
+	// 删除路演研究员
+	if len(delResearcherIdList) > 0 {
+		delResearcherIdStr := utils.Implode(delResearcherIdList)
+		sql := `DELETE FROM rs_calendar_researcher WHERE rs_calendar_researcher_id in (` + delResearcherIdStr + `) `
+		_, tmpErr := to.Raw(sql).Exec()
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+	}
+
+	// 修改路演研究员
+	for _, rsCalendarResearcher := range updateRsCalendarResearcherList {
+		rsCalendarResearcher.StartDate = currentStartTimer.Format(utils.FormatDate)
+		rsCalendarResearcher.EndDate = currentEndTimer.Format(utils.FormatDate)
+		rsCalendarResearcher.StartTime = currentStartTimer.Format(utils.FormatTime)
+		rsCalendarResearcher.EndTime = currentEndTimer.Format(utils.FormatTime)
+		rsCalendarResearcher.StartWeek = utils.EnWeekToCnWeek(currentStartTimer.Weekday().String())
+		rsCalendarResearcher.EndWeek = utils.EnWeekToCnWeek(currentEndTimer.Weekday().String())
+		rsCalendarResearcher.Status = 2 // 已接受
+		rsCalendarResearcher.ModifyTime = time.Now()
+		rsCalendarResearcher.IsSynced = 1
+		_, tmpErr := to.Update(rsCalendarResearcher, "StartDate", "EndDate", "StartTime", "EndTime", "StartWeek", "EndWeek", "Status", "ModifyTime", "IsSynced")
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+	}
+
+	// 路演研究员入库
+	rsCalendarResearcherList := make([]*RsCalendarResearcher, 0)
+	for _, researcheInfo := range addResearcherList {
+		rsCalendarResearcher := &RsCalendarResearcher{
+			RsCalendarResearcherId: 0,
+			RsCalendarId:           rsCalendar.RsCalendarId,
+			ResearcherId:           researcheInfo.AdminId,
+			ResearcherName:         researcheInfo.RealName,
+			StartDate:              currentStartTimer.Format(utils.FormatDate),
+			EndDate:                currentEndTimer.Format(utils.FormatDate),
+			StartTime:              currentStartTimer.Format(utils.FormatTime),
+			EndTime:                currentEndTimer.Format(utils.FormatTime),
+			StartWeek:              utils.EnWeekToCnWeek(currentStartTimer.Weekday().String()),
+			EndWeek:                utils.EnWeekToCnWeek(currentEndTimer.Weekday().String()),
+			CreateTime:             time.Now(),
+			ModifyTime:             time.Now(),
+			Status:                 2, //1:待接受,2:已接受,3:已拒绝,4:已删除,5:已撤回,6:已结束
+			RefuseReason:           "",
+			//RefuseTime:             time.Time{},
+			DeleteReason: "",
+			IsSynced:     1,
+		}
+		rsCalendarResearcherId, tmpErr := to.Insert(rsCalendarResearcher)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+		rsCalendarResearcher.RsCalendarResearcherId = int(rsCalendarResearcherId)
+		rsCalendarResearcherList = append(rsCalendarResearcherList, rsCalendarResearcher)
+	}
+
+	// 关系表变更
+	selfCalendarId := rsCalendarRelation.SelfCalendarId
+	if len(updateRsCalendarResearcherList) > 0 {
+		selfCalendarId = updateRsCalendarResearcherList[0].RsCalendarResearcherId //更新的研究员关系表id
+	} else if len(rsCalendarResearcherList) > 0 {
+		selfCalendarId = rsCalendarResearcherList[0].RsCalendarResearcherId //新增的研究员关系表id
+	}
+
+	//关系入库
+	customerId, _ := strconv.Atoi(thirdUserCalendar.CustomerId)
+	rsCalendarRelation.SelfCalendarId = selfCalendarId
+	rsCalendarRelation.UserId = thirdUserCalendar.UserId
+	rsCalendarRelation.UserPhone = thirdUserCalendar.UserPhone
+	rsCalendarRelation.UserName = thirdUserCalendar.UserName
+	rsCalendarRelation.ProjectName = thirdUserCalendar.ProjectName
+	rsCalendarRelation.ProjectId = thirdUserCalendar.ProjectId
+	rsCalendarRelation.CustomerId = customerId
+	rsCalendarRelation.CustomerName = thirdUserCalendar.CustomerName
+	rsCalendarRelation.CustomerSocial = thirdUserCalendar.CustomerSocial
+	rsCalendarRelation.ProjectType = thirdUserCalendar.ProjectType
+	rsCalendarRelation.ProjectFormType = thirdUserCalendar.ProjectType
+	rsCalendarRelation.Room = thirdUserCalendar.Room
+	rsCalendarRelation.StartTime = thirdUserCalendar.StartTime
+	rsCalendarRelation.EndTime = thirdUserCalendar.EndTime
+	rsCalendarRelation.Content = thirdUserCalendar.Content
+	rsCalendarRelation.FeedExpert = thirdUserCalendar.FeedExpert
+	rsCalendarRelation.Title = thirdUserCalendar.Title
+	rsCalendarRelation.ResearcherMobile = thirdUserCalendar.ResearcherMobile
+	rsCalendarRelation.ModifyTime = time.Now()
+	_, err = to.Update(rsCalendarRelation, "SelfCalendarId", "UserId", "UserPhone", "UserName", "ProjectName", "ProjectId", "CustomerId", "CustomerName", "CustomerSocial", "ProjectType", "ProjectFormType", "Room", "StartTime", "EndTime", "Content", "FeedExpert", "Title", "ResearcherMobile", "ModifyTime")
+	return
+}
+
+func SyncRsCalendarRelation(thirdUserCalendar UserCalendar, createUser *system.AdminItem, researcherList []*system.AdminItem) (err error) {
+	currentStartTimer := time.Unix(int64(thirdUserCalendar.StartTime), 0)
+	currentEndTimer := time.Unix(int64(thirdUserCalendar.EndTime), 0)
+
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	//路演活动表入库
+	rsCalendar := &RsCalendar{
+		SysUserId:        createUser.AdminId,
+		SysUserRealName:  createUser.RealName,
+		ActivityType:     "路演",
+		RoadshowType:     "",
+		RoadshowPlatform: "",
+		CompanyId:        0,
+		CompanyName:      thirdUserCalendar.CustomerName,
+		Province:         "",
+		ProvinceCode:     "",
+		City:             "",
+		CityCode:         "",
+		Theme:            "",
+		CooperationName:  "",
+		Title:            thirdUserCalendar.Title,
+		Source:           1, //来源,0:自系统,1:上海方的
+		CreateTime:       time.Now(),
+		ModifyTime:       time.Now(),
+		ActivityCategory: "",
+		IsSynced:         1,
+	}
+	rsCalendarId, err := to.Insert(rsCalendar)
+	if err != nil {
+		return
+	}
+	rsCalendar.RsCalendarId = int(rsCalendarId)
+
+	// 路演研究员入库
+	rsCalendarResearcherList := make([]*RsCalendarResearcher, 0)
+	for _, researcheInfo := range researcherList {
+		rsCalendarResearcher := &RsCalendarResearcher{
+			RsCalendarResearcherId: 0,
+			RsCalendarId:           rsCalendar.RsCalendarId,
+			ResearcherId:           researcheInfo.AdminId,
+			ResearcherName:         researcheInfo.RealName,
+			StartDate:              currentStartTimer.Format(utils.FormatDate),
+			EndDate:                currentEndTimer.Format(utils.FormatDate),
+			StartTime:              currentStartTimer.Format(utils.FormatTime),
+			EndTime:                currentEndTimer.Format(utils.FormatTime),
+			StartWeek:              utils.EnWeekToCnWeek(currentStartTimer.Weekday().String()),
+			EndWeek:                utils.EnWeekToCnWeek(currentEndTimer.Weekday().String()),
+			CreateTime:             time.Now(),
+			ModifyTime:             time.Now(),
+			Status:                 2, //1:待接受,2:已接受,3:已拒绝,4:已删除,5:已撤回,6:已结束
+			RefuseReason:           "",
+			//RefuseTime:             time.Time{},
+			DeleteReason: "",
+			IsSynced:     1,
+		}
+		rsCalendarResearcherId, tmpErr := to.Insert(rsCalendarResearcher)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+		rsCalendarResearcher.RsCalendarResearcherId = int(rsCalendarResearcherId)
+		rsCalendarResearcherList = append(rsCalendarResearcherList, rsCalendarResearcher)
+	}
+
+	selfCalendarId := 0
+	if len(rsCalendarResearcherList) > 0 {
+		selfCalendarId = rsCalendarResearcherList[0].RsCalendarResearcherId
+	}
+	//关系入库
+	customerId, _ := strconv.Atoi(thirdUserCalendar.CustomerId)
+	rsCalendarRelation := &RsCalendarRelation{
+		//RelationId:       0,
+		CalendarType:     1,              //日历类型;1:路演;2:事项
+		SelfCalendarId:   selfCalendarId, //研究员与路演关系id;
+		ThirdCalendarId:  thirdUserCalendar.ID,
+		UserId:           thirdUserCalendar.UserId,
+		UserPhone:        thirdUserCalendar.UserPhone,
+		UserName:         thirdUserCalendar.UserName,
+		ProjectName:      thirdUserCalendar.ProjectName,
+		ProjectId:        thirdUserCalendar.ProjectId,
+		CustomerId:       customerId,
+		CustomerName:     thirdUserCalendar.CustomerName,
+		CustomerSocial:   thirdUserCalendar.CustomerSocial,
+		ProjectType:      thirdUserCalendar.ProjectType,
+		ProjectFormType:  thirdUserCalendar.ProjectType,
+		Room:             thirdUserCalendar.Room,
+		StartTime:        thirdUserCalendar.StartTime,
+		EndTime:          thirdUserCalendar.EndTime,
+		Content:          thirdUserCalendar.Content,
+		FeedExpert:       thirdUserCalendar.FeedExpert,
+		Title:            thirdUserCalendar.Title,
+		ResearcherMobile: thirdUserCalendar.ResearcherMobile,
+		ModifyTime:       time.Now(),
+		CreateTime:       time.Now(),
+	}
+	rsCalendarRelationId, err := to.Insert(rsCalendarRelation)
+	if err != nil {
+		return
+	}
+	rsCalendarRelation.RelationId = int(rsCalendarRelationId)
+
+	return
+}
+
+// AddRsCalendarRelation 添加自系统路演与第三方路演关系
+func AddRsCalendarRelation(item *RsCalendarRelation) (lastId int64, err error) {
+	o := orm.NewOrm()
+	lastId, err = o.Insert(item)
+	return
+}

+ 51 - 0
models/roadshow/shanghai_calendar.go

@@ -0,0 +1,51 @@
+package roadshow
+
+type GetTokenResp struct {
+	Code      int       `json:"code"`
+	Msg       string    `json:"msg"`
+	Time      int       `json:"time"`
+	TokenData TokenData `json:"data"`
+}
+
+type UserCalendar struct {
+	ID               int    `json:"id" description:"关系id" `
+	UserId           int    `json:"user_id" description:"创建人id" `
+	UserPhone        string `json:"user_phone" description:"创建人手机号" `
+	UserName         string `json:"user_name" description:"创建人昵称" `
+	ProjectName      string `json:"project_name" description:"活动名称" `
+	ProjectId        int    `json:"project_id" description:"活动id" `
+	CustomerId       string `json:"customer_id" description:"客户id" `
+	CustomerName     string `json:"customer_name" description:"客户名称" `
+	CustomerSocial   string `json:"customer_social" description:"客户社会信用码" `
+	ProjectType      int    `json:"project_type" description:"活动类型:1=沙龙,2=路演,3=专家需求,4=研究需求,5=电话,6=面谈,7=专题需求,8=线下沙龙,9=公司调研"`
+	ProjectFormType  int    `json:"project_form_type" description:"服务形式:1=沙龙,2=路演,3=专家需求,4=研究需求,5=电话,6=面谈,7=专题需求"`
+	Room             int    `json:"room" description:"会议室id"`
+	StartTime        int    `json:"start_time" description:"开始时间戳"`
+	EndTime          int    `json:"end_time" description:"结束时间戳"`
+	Content          string `json:"content" description:"活动内容"`
+	FeedExpert       string `json:"feed_expert" description:"邀请的专家"`
+	Title            string `json:"title" description:"日历显示的标题"`
+	ResearcherMobile string `json:"researcher_mobile" description:"研究员+协同人员手机号(多个使用逗号拼接)"`
+}
+
+type TokenData struct {
+	AccessToken string `json:"access_token"`
+	ExpireIn    int    `json:"expire_in"`
+}
+
+type UserCalendarList struct {
+	Code int            `json:"code"`
+	Msg  string         `json:"msg"`
+	Time int            `json:"time"`
+	Data []UserCalendar `json:"data"`
+}
+
+type CreatSHCalendarResp struct {
+	Code int      `json:"code"`
+	Msg  string   `json:"msg"`
+	Time int      `json:"time"`
+	Data Calendar `json:"data"`
+}
+type Calendar struct {
+	CalendarID string `json:"calendar_id"`
+}

+ 52 - 0
models/system/sys_admin.go

@@ -0,0 +1,52 @@
+package system
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+type AdminItem struct {
+	AdminId                 int    `description:"系统用户id"`
+	AdminName               string `description:"系统用户名称"`
+	RealName                string `description:"系统用户姓名"`
+	Password                string
+	LastUpdatedPasswordTime string `json:"-"`
+	Enabled                 int    `description:"1:有效,0:禁用"`
+	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 `json:"-" description:"部门名称"`
+	TeamId                  int    `description:"三级id"`
+	GroupId                 int    `description:"分组id"`
+	GroupName               string `json:"-" description:"分组名称"`
+	Authority               int    `description:"管理权限,0:无,1:部门负责人,2:小组负责人,或者ficc销售主管,4:ficc销售组长"`
+	Position                string `description:"职位"`
+	DepartmentGroup         string `description:"部门分组"`
+	LabelVal                int    `description:"标签:1:超级管理员,2:管理员,3:部门经理,4:组长,5:ficc销售主管"`
+	ResearchGroupName       string `description:"研究方向分组名称"`
+	Province                string `description:"省"`
+	ProvinceCode            string `description:"省编码"`
+	City                    string `description:"市"`
+	CityCode                string `description:"市编码"`
+	EmployeeId              string `description:"员工工号(钉钉/每刻报销)"`
+	TelAreaCode             string `description:"手机区号"`
+}
+
+func GetSysuserList(condition string, pars []interface{}, startSize, pageSize int) (items []*AdminItem, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM admin WHERE 1=1 `
+	if condition != "" {
+		sql += condition
+	}
+	sql += `ORDER BY enabled DESC, last_updated_time DESC, created_time DESC LIMIT ?,?`
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}

+ 740 - 0
services/roadshow/calendar.go

@@ -2,8 +2,21 @@ package roadshow
 
 import (
 	"context"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"github.com/rdlucklib/rdluck_tools/http"
+	"hongze/hongze_task/models"
+	"hongze/hongze_task/models/cygx"
 	"hongze/hongze_task/models/roadshow"
+	"hongze/hongze_task/models/system"
+	"hongze/hongze_task/services/alarm_msg"
 	"hongze/hongze_task/utils"
+	"io/ioutil"
+	netHttp "net/http"
+	"net/url"
+	"strconv"
+	"strings"
 	"sync"
 	"time"
 )
@@ -38,3 +51,730 @@ func ModifyRsCalendarResearcherStatus(cont context.Context) (err error) {
 	lock.Unlock()
 	return err
 }
+
+// ShResponse 上海数据返回结构体
+type ShResponse struct {
+	Code int         `json:"code" description:"1:正常返回;4001:token失效"`
+	Msg  string      `json:"msg" description:"文字描述"`
+	Time int         `json:"time" description:"时间戳"`
+	Data interface{} `json:"data" description:"业务数据"`
+}
+
+// getCurl get请求上海接口
+func getCurl(urlStr string, params url.Values, num int) (body []byte, err error) {
+	if err != nil {
+		go alarm_msg.SendAlarmMsg("get请求上海接口失败,ERR:"+err.Error(), 3)
+		//go utils.SendEmail(utils.APPNAME+"get请求上海接口失败:"+time.Now().Format("2006-01-02 15:04:05"), "get请求上海接口失败:"+err.Error(), utils.EmailSendToUsers)
+	}
+	token, err := GetAccessToken(false)
+	if err != nil {
+		return
+	}
+	params.Add("access_token", token)
+	getUrl := urlStr + "?" + params.Encode()
+	body, err = http.Get(getUrl)
+	if err != nil {
+		utils.FileLog.Info(fmt.Sprint("get Err:", err.Error(), ";url:", getUrl, ";params:", params.Encode(), ";response:", string(body)))
+		err = errors.New("NewRequest Err:" + err.Error())
+		return
+	}
+
+	var response ShResponse
+	err = json.Unmarshal(body, &response)
+	if err != nil {
+		utils.FileLog.Info(fmt.Sprint("get Err:", err.Error(), ";url:", getUrl, ";params:", params.Encode(), ";response:", string(body)))
+		err = errors.New("Unmarshal Err:" + err.Error())
+		return
+	}
+
+	//如果是token失效,同时只是第一次请求(没有尝试强制刷新token,那么重新请求)
+	if response.Code == 4001 && num <= 0 {
+		utils.FileLog.Info(fmt.Sprint("get data err", ";url:", getUrl, ";params:", params.Encode(), ";response:", string(body)))
+
+		//token失效
+		_, tmpErr := refreshAccessToken()
+		if tmpErr != nil {
+			err = tmpErr
+		}
+		num++
+		params.Del("access_token")
+		return getCurl(urlStr, params, num)
+	} else if response.Code != 1 {
+		utils.FileLog.Info(fmt.Sprint("get data err", ";url:", getUrl, ";params:", params.Encode(), ";response:", string(body)))
+		err = errors.New(response.Msg)
+		return
+	}
+	return
+}
+
+// postCurl post请求上海接口
+func postCurl(urlStr string, form url.Values, num int) (body []byte, err error, errMsg string) {
+	logMsg := ``
+	defer func() {
+		if err != nil {
+			if logMsg != `` {
+				errMsg = logMsg
+				go alarm_msg.SendAlarmMsg("post请求上海接口失败,ERR:"+err.Error()+";errMsg:"+errMsg, 3)
+				//go utils.SendEmail(utils.APPNAME+"post请求上海接口失败:"+time.Now().Format("2006-01-02 15:04:05"), "post请求上海接口失败:"+errMsg, utils.EmailSendToUsers)
+			}
+		}
+	}()
+	token, err := GetAccessToken(false)
+	if err != nil {
+		return
+	}
+	finalUrl := urlStr + "?access_token=" + token
+
+	//发送创建请求
+	resp, err := netHttp.PostForm(finalUrl, form)
+	if err != nil {
+		err = errors.New("NewRequest Err:" + err.Error())
+		return
+	}
+
+	defer resp.Body.Close()
+
+	//解析resp并且存入关联表
+	body, err = ioutil.ReadAll(resp.Body)
+	if err != nil {
+		logMsg = fmt.Sprint("post err; request:", form.Encode(), "; errMsg:", err.Error())
+		utils.FileLog.Info(logMsg)
+		return
+	}
+	logMsg = fmt.Sprint("post request:", form.Encode(), "; response:", string(body))
+	utils.FileLog.Info(logMsg)
+
+	var response ShResponse
+	err = json.Unmarshal(body, &response)
+	if err != nil {
+		utils.FileLog.Info("post Err:", err.Error(), ";url:", finalUrl, ";params:", form.Encode(), ";response:", string(body))
+		err = errors.New("Unmarshal Err:" + err.Error())
+		return
+	}
+	utils.FileLog.Info(fmt.Sprint("post request url:", finalUrl, ";params:", form.Encode(), ";response:", string(body)))
+
+	//如果是token失效,同时只是第一次请求(没有尝试强制刷新token,那么重新请求)
+	if response.Code == 4001 && num <= 0 {
+		//token失效
+		_, tmpErr := refreshAccessToken()
+		if tmpErr != nil {
+			err = tmpErr
+		}
+		num++
+		return postCurl(urlStr, form, num)
+	} else if response.Code != 1 {
+		utils.FileLog.Info(fmt.Sprint("post data err", ";url:", finalUrl, ";params:", form.Encode(), ";response:", string(body)))
+		err = errors.New(response.Msg)
+		return
+	}
+	return
+}
+
+// GetAccessToken 获取accessToken
+func GetAccessToken(isRefresh bool) (token string, err error) {
+	defer func() {
+		if err != nil {
+			go alarm_msg.SendAlarmMsg("获取上海的token失败,ERR:"+err.Error(), 3)
+			//go utils.SendEmail(utils.APPNAME+"获取上海的token失败:"+time.Now().Format("2006-01-02 15:04:05"), err.Error(), utils.EmailSendToUsers)
+		}
+	}()
+	token, redisErr := utils.Rc.RedisString("SH_ACCESS_TOKEN")
+	//如果从redis中accessToken 获取失败或者token为空了,再或者需要强制刷新了,那么重新获取accessToken
+	if redisErr != nil || token == `` || isRefresh {
+		return refreshAccessToken()
+	}
+	return
+}
+
+// getAccessToken token内部请求接口
+func getAccessToken() (tokenData roadshow.TokenData, err error) {
+	defer func() {
+		if err != nil {
+			go alarm_msg.SendAlarmMsg("更新上海的token失败;ERR:"+err.Error(), 3)
+			//go utils.SendEmail(utils.APPNAME+"更新上海的token失败:"+time.Now().Format("2006-01-02 15:04:05"), err.Error(), utils.EmailSendToUsers)
+		}
+	}()
+	getUrl := fmt.Sprintf(utils.CRM_OPEN_API_URL+"/v1/auth/getAccessToken?app_key=%s&app_secret=%s", utils.CRM_OPEN_API_APP_KEY, utils.CRM_OPEN_API_APP_SECRET)
+	body, err := http.Get(getUrl)
+	if err != nil {
+		err = errors.New("NewRequest Err:" + err.Error())
+		return
+	}
+
+	var tokenResp roadshow.GetTokenResp
+	err = json.Unmarshal(body, &tokenResp)
+	if err != nil {
+		err = errors.New("Unmarshal Err:" + err.Error())
+		return
+	}
+	if tokenResp.Code != 1 {
+		err = errors.New("getAccessToken err:" + tokenResp.Msg)
+		return
+	}
+	tokenData = tokenResp.TokenData
+	return
+}
+
+// refreshAccessToken 强制刷新获取accessToken
+func refreshAccessToken() (token string, err error) {
+	defer func() {
+		if err != nil {
+			go alarm_msg.SendAlarmMsg("刷新上海的token失败;ERR:"+err.Error(), 3)
+			//go utils.SendEmail(utils.APPNAME+"刷新上海的token失败:"+time.Now().Format("2006-01-02 15:04:05"), err.Error(), utils.EmailSendToUsers)
+		}
+	}()
+	tokenInfo, tmpErr := getAccessToken()
+	if tmpErr != nil {
+		err = tmpErr
+		return
+	}
+	token = tokenInfo.AccessToken
+
+	//token存入redis
+	err = utils.Rc.Put("SH_ACCESS_TOKEN", token, time.Duration(tokenInfo.ExpireIn-600)*time.Second)
+	if err != nil {
+		go alarm_msg.SendAlarmMsg("获取上海的token失败;上海token存入redis失败,ERR:"+err.Error(), 3)
+		//go utils.SendEmail(utils.APPNAME+"获取上海的token失败:"+time.Now().Format("2006-01-02 15:04:05"), "上海token存入redis失败:", utils.EmailSendToUsers)
+	}
+	return
+}
+
+// getCalendarFrom 获取上海方的路演活动列表
+func getCalendarFrom(userPhone, startDate, endDate string) (list []roadshow.UserCalendar, err error) {
+	exUrl := utils.CRM_OPEN_API_URL + "/v1/calendar/userCalendarList"
+	form := url.Values{
+		"user_phone": {userPhone},
+		"start_time": {startDate},
+		"end_time":   {endDate},
+	}
+	body, err := getCurl(exUrl, form, 0)
+	if err != nil {
+		err = errors.New("NewRequest Err:" + err.Error())
+		return
+	}
+	var userCalendarList roadshow.UserCalendarList
+	err = json.Unmarshal(body, &userCalendarList)
+	if err != nil {
+		err = errors.New("Unmarshal Err:" + err.Error())
+		return
+	}
+	list = userCalendarList.Data
+	return
+}
+
+//func init() {
+//	SyncCalendarFromShanghai()
+//}
+
+// SyncCalendarFromShanghai 上海路演数据同步到自系统
+func SyncCalendarFromShanghai(cont context.Context) (err error) {
+	errMsgList := make([]string, 0)
+	defer func() {
+		if len(errMsgList) > 0 {
+			//fmt.Println("err:", errMsg)
+			if err != nil {
+				errMsgList = append(errMsgList, fmt.Sprint("ERR:"+err.Error(), ";"))
+			}
+			go alarm_msg.SendAlarmMsg("上海路演数据同步到自系统失败;errMsg:"+strings.Join(errMsgList, "\n"), 3)
+		}
+	}()
+
+	var mobiles []string
+	mobilesMap := make(map[string]bool)
+	researcherListAdmin, tmpErr := roadshow.GetResearcher() // ficc研究员
+	if tmpErr != nil {
+		errMsgList = append(errMsgList, fmt.Sprint("获取研究员信息失败:GetResearcher", ";err:"+tmpErr.Error(), ";"))
+		return
+	}
+	for _, v := range researcherListAdmin {
+		if v.Mobile == "" {
+			continue
+		}
+		if mobilesMap[v.Mobile] {
+			continue
+		}
+		mobiles = append(mobiles, v.Mobile)
+		mobilesMap[v.Mobile] = true
+	}
+	askUserList, tmpErr := cygx.GetAskEmailList() //权益研究员
+	if tmpErr != nil {
+		errMsgList = append(errMsgList, fmt.Sprint("获取研究员信息失败:GetAskEmailList", ";err:"+tmpErr.Error(), ";"))
+		return
+	}
+	for _, v := range askUserList {
+		if v.Mobile == "" {
+			continue
+		}
+		if mobilesMap[v.Mobile] {
+			continue
+		}
+		mobiles = append(mobiles, v.Mobile)
+		mobilesMap[v.Mobile] = true
+	}
+
+	adminList, tmpErr := models.GetAdminByAdminAllList() //查询管理员信息列表
+	if tmpErr != nil {
+		errMsgList = append(errMsgList, fmt.Sprint("查询管理员信息列表 失败:GetAdminByAdminAllList", ";err:"+tmpErr.Error(), ";"))
+		return
+	}
+	mapUserInfo := make(map[string]*system.AdminItem)
+	mapAdminIdMobil := make(map[int]string) // 研究员ID与手机号的关系
+	for _, v := range adminList {
+		if v.Mobile == "" {
+			continue
+		}
+		mapUserInfo[v.Mobile] = v
+		mapAdminIdMobil[v.AdminId] = v.Mobile
+	}
+	//获取前后三十天的信息
+	startDate := time.Now().AddDate(0, 0, -30).Format(utils.FormatDate)
+	endDate := time.Now().AddDate(0, 0, 30).Format(utils.FormatDate)
+	var userPhone string
+	var list []roadshow.UserCalendar
+	//获取指定日期内所有研究员的路演信息
+	for _, v := range mobiles {
+		if v == "" {
+			continue
+		}
+		time.Sleep(200 * time.Millisecond) // 加一个延迟0.2秒
+		userPhone = v
+		//以当前日期作为起始日期去同步
+		listSh, tmpErr := getCalendarFrom(userPhone, startDate, endDate)
+
+		if tmpErr != nil && tmpErr.Error() != "NewRequest Err:该用户不存在" {
+			errMsgList = append(errMsgList, fmt.Sprint("获取第三方路演日历数据失败,", "userPhone:", userPhone, ";currDay:", startDate, ";endDate:", endDate, ";err:"+tmpErr.Error()))
+			continue
+		}
+		for _, vSh := range listSh {
+			list = append(list, vSh)
+		}
+	}
+
+	//根据研究员和开始/结束日期获取上海的活动路演
+	rsCalendarResearcherList, tmpErr := roadshow.GetRsCalendarResearcherInfoIByResearcherIdAndDate(startDate, endDate)
+	if tmpErr != nil {
+		errMsgList = append(errMsgList, fmt.Sprint("根据研究员和开始/结束日期获取上海的活动路演 失败:时间段:", startDate, endDate, ";err:"+tmpErr.Error(), ";"))
+		return
+	}
+	//return
+	//待删除的活动路演
+	deleteRsCalendarResearcherMap := make(map[int][]*roadshow.RsCalendarResearcherRelationInfo)
+	for _, v := range rsCalendarResearcherList {
+		tmpList, ok := deleteRsCalendarResearcherMap[v.ThirdCalendarId]
+		if !ok {
+			tmpList = make([]*roadshow.RsCalendarResearcherRelationInfo, 0)
+		}
+		tmpList = append(tmpList, v)
+
+		deleteRsCalendarResearcherMap[v.ThirdCalendarId] = tmpList
+	}
+
+	thirdIdList := make([]int, 0)
+	mapRsCalendar := make(map[int]*roadshow.RsCalendar)
+
+	if len(list) > 0 {
+		for _, v := range list {
+			thirdIdList = append(thirdIdList, v.ID)
+			//移除还存在的活动,保留不存在的活动
+			delete(deleteRsCalendarResearcherMap, v.ID)
+		}
+		rsCalendarRelationList, tmpErr := roadshow.GetRsCalendarRelationListByThirdIds(thirdIdList)
+		if tmpErr != nil {
+			err = tmpErr
+			errMsgList = append(errMsgList, "获取关联列表失败,err:"+tmpErr.Error())
+			return
+		}
+		//已存在的三方日历信息,做对比使用
+		rsCalendarRelationMap := make(map[int]*roadshow.RsCalendarRelation)
+		var selfCalendarIds []int
+		for _, rsCalendarRelation := range rsCalendarRelationList {
+			rsCalendarRelationMap[rsCalendarRelation.ThirdCalendarId] = rsCalendarRelation
+			selfCalendarIds = append(selfCalendarIds, rsCalendarRelation.SelfCalendarId)
+		}
+		rsCalendarResearcherInfoList, tmpErr := roadshow.GetRsCalendarResearcherByIds(selfCalendarIds)
+		if tmpErr != nil {
+			errMsgList = append(errMsgList, fmt.Sprint("第三方日历ID:", selfCalendarIds, "获取路演研究员信息失败;err:"+tmpErr.Error(), ";"))
+			return
+		}
+		maprsCalendarResearcherInfo := make(map[int]*roadshow.RsCalendarResearcher)
+
+		var rsCalendarIds []int
+		for _, v := range rsCalendarResearcherInfoList {
+			maprsCalendarResearcherInfo[v.RsCalendarResearcherId] = v
+			rsCalendarIds = append(rsCalendarIds, v.RsCalendarId)
+		}
+
+		//获取研究员map数据信息
+		rsCalendarResearcherListDate, tmpErr := roadshow.GetRsCalendarResearcherListByRsCalendarIds(rsCalendarIds)
+		if tmpErr != nil {
+			errMsgList = append(errMsgList, fmt.Sprint("获取路演活动中的研究员列表失败,路演活动ID:", rsCalendarIds, ";err:"+tmpErr.Error(), ";"))
+			return
+		}
+		maprsCalendarResearcherList := make(map[int][]*roadshow.RsCalendarResearcher)
+		for _, v := range rsCalendarResearcherListDate {
+			maprsCalendarResearcherList[v.RsCalendarId] = append(maprsCalendarResearcherList[v.RsCalendarId], v)
+		}
+
+		//获取路演map信息
+		rsCalendarInfoList, tmpErr := roadshow.GetRsCalendarByIds(rsCalendarIds)
+		if tmpErr != nil {
+			errMsgList = append(errMsgList, fmt.Sprint("日历ID:", rsCalendarIds, "获取路演信息失败;err:"+tmpErr.Error(), ";"))
+			return
+		}
+
+		for _, v := range rsCalendarInfoList {
+			mapRsCalendar[v.RsCalendarId] = v
+		}
+		for _, v := range list {
+			//展示优先级:1、customer_name 2、project_name 3、title 需求池953
+			if v.CustomerName != "" {
+				v.Title = v.CustomerName
+			} else if v.ProjectName != "" {
+				v.Title = v.ProjectName
+			}
+			if rsCalendarRelation, ok := rsCalendarRelationMap[v.ID]; ok {
+				//存在的话,那么就去查找对应的信息
+				if rsCalendarRelation.CalendarType == 1 {
+					if maprsCalendarResearcherInfo[rsCalendarRelation.SelfCalendarId] == nil {
+						continue
+					}
+					rsCalendarResearcherInfo := maprsCalendarResearcherInfo[rsCalendarRelation.SelfCalendarId]
+					if mapRsCalendar[rsCalendarResearcherInfo.RsCalendarId] == nil {
+						continue
+					}
+					rsCalendarInfo := mapRsCalendar[rsCalendarResearcherInfo.RsCalendarId]
+					if rsCalendarInfo.Source == 0 { //自系统创建的路演活动,不需要依靠上海方来修改
+						continue
+					}
+
+					//是否去同步变更数据库
+					isUpdateSync := false
+
+					//是否变更
+					isUpdate := false
+					if v.StartTime != rsCalendarRelation.StartTime {
+						isUpdate = true
+					}
+					if v.EndTime != rsCalendarRelation.EndTime {
+						isUpdate = true
+					}
+					if v.Title != rsCalendarRelation.Title {
+						isUpdate = true
+					}
+					if rsCalendarResearcherInfo.Status == 4 { // 被删除了,现在是需要更新
+						isUpdate = true
+					}
+
+					researcherList := make([]*system.AdminItem, 0)
+					researcherMap := make(map[int]*system.AdminItem)
+
+					addResearcherList := make([]*system.AdminItem, 0)                 // 需要新增的研究员
+					delResearcherIdList := make([]int, 0)                             //需要删除的路演活动与研究员的关系id
+					updateResearcherList := make([]*roadshow.RsCalendarResearcher, 0) //待更新的路演活动中研究员信息
+
+					if v.ResearcherMobile != rsCalendarRelation.ResearcherMobile {
+						//研究员变更了,需要去改表
+						isUpdateSync = true
+						researcherMobileList := strings.Split(v.ResearcherMobile, ",")
+						if len(researcherMobileList) > 0 {
+							for _, mobile := range researcherMobileList {
+								if mapUserInfo[mobile] == nil {
+									continue
+								}
+								researcherInfo := mapUserInfo[mobile]
+								if researcherInfo.RoleTypeCode == utils.ROLE_TYPE_CODE_FICC_RESEARCHR ||
+									researcherInfo.RoleTypeCode == utils.ROLE_TYPE_CODE_RESEARCHR ||
+									researcherInfo.RoleTypeCode == utils.ROLE_TYPE_CODE_RAI_RESEARCHR ||
+									researcherInfo.RoleTypeCode == utils.ROLE_TYPE_CODE_FICC_DEPARTMENT ||
+									researcherInfo.RoleTypeCode == utils.ROLE_TYPE_CODE_RAI_DEPARTMENT ||
+									researcherInfo.RoleTypeCode == utils.ROLE_TYPE_CODE_RAI_SELLER ||
+									researcherInfo.RoleTypeCode == utils.ROLE_TYPE_CODE_RAI_ADMIN {
+									researcherList = append(researcherList, researcherInfo)
+									researcherMap[researcherInfo.AdminId] = researcherInfo
+								}
+							}
+						}
+
+						//没有研究员
+						if len(researcherList) <= 0 {
+							errMsgList = append(errMsgList, fmt.Sprint("第三方日历ID:", v.ID, ";对方研究员信息失败;"))
+							continue
+						}
+						if len(maprsCalendarResearcherList[rsCalendarInfo.RsCalendarId]) == 0 {
+							continue
+						}
+						rsCalendarResearcherListUpdate := maprsCalendarResearcherList[rsCalendarInfo.RsCalendarId] // 活动中包含的多个研究员
+
+						//现有活动中的研究员
+						rsCalendarResearcherMap := make(map[int]*roadshow.RsCalendarResearcher)
+						for _, rsCalendarResearcher := range rsCalendarResearcherListUpdate {
+							if _, ok := researcherMap[rsCalendarResearcher.ResearcherId]; ok {
+								if isUpdate {
+									updateResearcherList = append(updateResearcherList, rsCalendarResearcher)
+								}
+							} else {
+								delResearcherIdList = append(delResearcherIdList, rsCalendarResearcher.RsCalendarResearcherId)
+							}
+
+							rsCalendarResearcherMap[rsCalendarResearcher.ResearcherId] = rsCalendarResearcher
+						}
+
+						//校验是否需要新增研究员
+						for adminId, researcherInfo := range researcherMap {
+							//如果
+							if _, ok := rsCalendarResearcherMap[adminId]; !ok {
+								addResearcherList = append(addResearcherList, researcherInfo)
+							}
+						}
+					} else if isUpdate { //如果有字段更新,那么需要将所有的研究员信息更新
+						isUpdateSync = true
+						if len(maprsCalendarResearcherList[rsCalendarInfo.RsCalendarId]) == 0 {
+							continue
+						}
+						rsCalendarResearcherListUpdate := maprsCalendarResearcherList[rsCalendarInfo.RsCalendarId]
+						for _, rsCalendarResearcher := range rsCalendarResearcherListUpdate {
+							updateResearcherList = append(updateResearcherList, rsCalendarResearcher)
+						}
+					}
+
+					if isUpdateSync {
+						time.Sleep(200 * time.Millisecond) // 加一个延迟0.2秒
+						tmpErr = roadshow.UpdateSyncRsCalendarRelation(v, rsCalendarInfo, rsCalendarRelation, updateResearcherList, delResearcherIdList, addResearcherList)
+						if tmpErr != nil {
+							err = tmpErr
+							errMsgList = append(errMsgList, fmt.Sprint("第三方日历ID:", v.ID, "修改关联关系失败;err:"+tmpErr.Error(), ";"))
+							return
+						}
+					}
+				} else {
+					//事项
+					//事项都是由自系统创建的,不需要依靠上海方来修改
+				}
+			} else {
+				//fmt.Println("add")
+				if mapUserInfo[v.UserPhone] == nil {
+					continue
+				}
+				createUser := mapUserInfo[v.UserPhone]
+				//研究员列表
+				researcherList := make([]*system.AdminItem, 0)
+				researcherMobileList := strings.Split(v.ResearcherMobile, ",")
+				if len(researcherMobileList) > 0 {
+					for _, mobile := range researcherMobileList {
+						if mapUserInfo[mobile] == nil {
+							continue
+						}
+						researcherInfo := mapUserInfo[mobile]
+						if researcherInfo.RoleTypeCode == utils.ROLE_TYPE_CODE_FICC_RESEARCHR ||
+							researcherInfo.RoleTypeCode == utils.ROLE_TYPE_CODE_RESEARCHR ||
+							researcherInfo.RoleTypeCode == utils.ROLE_TYPE_CODE_RAI_RESEARCHR ||
+							researcherInfo.RoleTypeCode == utils.ROLE_TYPE_CODE_FICC_DEPARTMENT ||
+							researcherInfo.RoleTypeCode == utils.ROLE_TYPE_CODE_RAI_DEPARTMENT ||
+							researcherInfo.RoleTypeCode == utils.ROLE_TYPE_CODE_RAI_SELLER ||
+							researcherInfo.RoleTypeCode == utils.ROLE_TYPE_CODE_RAI_ADMIN ||
+							researcherInfo.RoleTypeCode == utils.ROLE_TYPE_CODE_FICC_ADMIN {
+							researcherList = append(researcherList, researcherInfo)
+						}
+					}
+				}
+
+				//没有研究员
+				if len(researcherList) <= 0 {
+					continue
+				}
+				//数据入库
+				time.Sleep(200 * time.Millisecond) // 加一个延迟0.2秒
+				tmpErr = roadshow.SyncRsCalendarRelation(v, createUser, researcherList)
+				if tmpErr != nil {
+					err = tmpErr
+					errMsgList = append(errMsgList, fmt.Sprint("第三方日历ID:", v.ID, "绑定关联关系失败;err:"+tmpErr.Error(), ";"))
+					continue
+				}
+			}
+		}
+	}
+
+	var delRsCalendarResearcherIds []int
+	////上海那边已经删除了路演,这边也要同步删除
+	for _, deleteRsCalendarResearcherList := range deleteRsCalendarResearcherMap {
+		for _, deleteRsCalendarResearcher := range deleteRsCalendarResearcherList {
+			delRsCalendarResearcherIds = append(delRsCalendarResearcherIds, deleteRsCalendarResearcher.RsCalendarResearcherId)
+		}
+	}
+
+	if len(delRsCalendarResearcherIds) > 0 {
+		tmpErr := roadshow.ModifyRsCalendarResearcherStatusDel(delRsCalendarResearcherIds)
+		if tmpErr != nil {
+			err = tmpErr
+			errMsgList = append(errMsgList, fmt.Sprint("删除第三方日历ID:", delRsCalendarResearcherIds, "删除关联关系失败;err:"+tmpErr.Error(), ";"))
+			return
+		}
+	}
+
+	mapShangHaiResearcher := make(map[string][]string)
+	for _, v := range list {
+		researcherMobileList := strings.Split(v.ResearcherMobile, ",")
+		for _, vm := range researcherMobileList {
+			mapShangHaiResearcher[vm] = append(mapShangHaiResearcher[vm], time.Unix(int64(v.StartTime), 0).Format(utils.FormatDateTime))
+		}
+	}
+
+	for _, v := range rsCalendarResearcherList {
+		mobileResearcher := mapAdminIdMobil[v.ResearcherId] //研究员手机号
+		if mobileResearcher == "" {
+			continue
+		}
+		//如果手机号对应的开始时间不存在,就把这场活动同步到上海那边
+		if !utils.InArrayByStr(mapShangHaiResearcher[mobileResearcher], fmt.Sprint(v.StartDate, " ", v.StartTime)) {
+			if mapRsCalendar[v.RsCalendarId] == nil {
+				continue
+			}
+			CalendarToSH(mapRsCalendar[v.RsCalendarId], v.ResearcherId)
+			time.Sleep(1000 * time.Millisecond) // 延迟1秒
+		}
+	}
+	return
+}
+
+// CalendarToSH 创建活动时同步上海的前置函数
+func CalendarToSH(rsCalendar *roadshow.RsCalendar, researcherId int) {
+	var err error
+	errMsg := ``
+	defer func() {
+		if err != nil {
+			errMsg = err.Error() + ";" + errMsg
+			fmt.Println(errMsg)
+			go alarm_msg.SendAlarmMsg("新建上海研究员日历失败,ERR:"+err.Error()+";errMsg:"+errMsg, 3)
+			//go utils.SendEmail(utils.APPNAME+"新建上海研究员日历失败:"+time.Now().Format("2006-01-02 15:04:05"), err.Error(), utils.EmailSendToUsers)
+		}
+	}()
+	//redis获取创建者及研究员信息
+	userInfo, err := getAdminInfoById(rsCalendar.SysUserId)
+	if err != nil {
+		utils.FileLog.Info("getAdminInfoById err: " + err.Error())
+		return
+	}
+	researcher, err := roadshow.GetRsCalendarResearcherByRsCalendarIdAndResearcherId(rsCalendar.RsCalendarId, researcherId)
+	if err != nil {
+		utils.FileLog.Info("getAdminInfoById err: " + err.Error())
+		return
+	}
+	researcherInfo, err := getAdminInfoById(researcher.ResearcherId)
+
+	if err != nil {
+		utils.FileLog.Info("getAdminInfoById err: " + err.Error())
+		return
+	}
+
+	sTime, _ := time.ParseInLocation(utils.FormatDateTime, researcher.StartDate+" "+researcher.StartTime, time.Now().Location())
+	startTime := sTime.Format("2006-01-02 15:04")
+	eTime, _ := time.ParseInLocation(utils.FormatDateTime, researcher.EndDate+" "+researcher.EndTime, time.Now().Location())
+	endTime := eTime.Format("2006-01-02 15:04")
+
+	// 创建上海路演日程
+	err, errMsg = creatSHCalendar(userInfo.Mobile, researcherInfo.Mobile, rsCalendar.Title, startTime, endTime, 1, researcher.RsCalendarResearcherId)
+	if err != nil {
+		utils.FileLog.Info("CreatSHCalendar err: " + err.Error())
+		fmt.Println(err)
+		return
+	}
+
+	// 更新路演与研究员关系表的同步字段
+	whereParams := make(map[string]interface{})
+	updateParams := make(map[string]interface{})
+	whereParams["rs_calendar_researcher_id"] = researcher.RsCalendarResearcherId
+	updateParams["is_synced"] = 1
+	updateParams["modify_time"] = time.Now()
+	err = roadshow.UpdateCalendarResearcher(whereParams, updateParams)
+	if err != nil {
+		utils.FileLog.Info("UpdateCalendarResearcher err: " + err.Error())
+		fmt.Println("UpdateCalendarResearcher err: " + err.Error())
+		return
+	}
+
+	// 更新路演的同步字段
+	calWhereParams := make(map[string]interface{})
+	calWhereParams["rs_calendar_id"] = rsCalendar.RsCalendarId
+	err = roadshow.UpdateRsCalendar(calWhereParams, updateParams)
+	if err != nil {
+		utils.FileLog.Info("UpdateRsCalendar err: " + err.Error())
+		fmt.Println("UpdateRsCalendar err: " + err.Error())
+		return
+	}
+}
+
+// creatSHCalendar 新增上海日历活动
+func creatSHCalendar(userPhone, toUserPhone, content, startTime, endTime string, calendarType int8, selfCalendarId int) (err error, errMsg string) {
+	logMsg := ``
+	defer func() {
+		if err != nil {
+			if logMsg != `` {
+				errMsg = logMsg
+			}
+		}
+	}()
+	finalUrl := utils.CRM_OPEN_API_URL + "/v1/Calendar/create"
+
+	form := url.Values{
+		"user_phone":    {userPhone},
+		"to_user_phone": {toUserPhone},
+		"content":       {content},
+		"start_time":    {startTime},
+		"end_time":      {endTime},
+	}
+	//发送创建请求
+	body, err, logMsg := postCurl(finalUrl, form, 0)
+	if err != nil {
+		err = errors.New("NewRequest Err:" + err.Error())
+		return
+	}
+	var creatSHCalendarResp roadshow.CreatSHCalendarResp
+	err = json.Unmarshal(body, &creatSHCalendarResp)
+	if err != nil {
+		err = errors.New("Unmarshal Err:" + err.Error())
+		return
+	}
+	calendar := creatSHCalendarResp.Data
+
+	//上海系统id
+	sThirdId := calendar.CalendarID
+	thirdId, err := strconv.Atoi(sThirdId)
+	if err != nil {
+		err = errors.New("string to int Err:" + err.Error())
+		return
+	}
+	// 添加自系统与上海系统的路演关系
+	relationItem := roadshow.RsCalendarRelation{
+		UserPhone:        userPhone,
+		CalendarType:     calendarType,
+		SelfCalendarId:   selfCalendarId,
+		ThirdCalendarId:  thirdId,
+		Title:            content,
+		ResearcherMobile: toUserPhone,
+		ModifyTime:       time.Now(),
+		CreateTime:       time.Now(),
+	}
+	_, err = roadshow.AddRsCalendarRelation(&relationItem)
+	if err != nil {
+		err = errors.New("AddRsCalendarRelation Err:" + err.Error())
+		fmt.Println(err)
+		return
+	}
+	return
+}
+
+func getAdminInfoById(sysId int) (adminInfo system.AdminItem, err error) {
+	adminMap := make(map[int]system.AdminItem)
+	list, err := system.GetSysuserList("", []interface{}{}, 0, 1000)
+	//	GetSysuserList
+	for _, tmpAdminInfo := range list {
+		adminMap[tmpAdminInfo.AdminId] = *tmpAdminInfo
+	}
+
+	adminInfo, ok := adminMap[sysId]
+	if !ok {
+		err = fmt.Errorf("找不到该用户id,sysId:" + fmt.Sprint(sysId))
+	}
+
+	return
+}

+ 4 - 0
services/task.go

@@ -304,6 +304,10 @@ func releaseTask() {
 	//问答社区定时发送模版消息给研究员
 	notifyNeedAnswerQuestionToResearch := task.NewTask("notifyNeedAnswerQuestionToResearch", "0 0 10 * * *", NotifyNeedAnswerQuestionToResearch)
 	task.AddTask("问答社区定时发送模版消息给研究员", notifyNeedAnswerQuestionToResearch)
+
+	//上海路演数据同步到自系统
+	syncCalendarFromShanghai := task.NewTask("syncCalendarFromShanghai", "0 0 * * * *", roadshow.SyncCalendarFromShanghai)
+	task.AddTask("上海路演数据同步到自系统", syncCalendarFromShanghai)
 }
 
 //func TaskTest() {

+ 34 - 5
utils/common.go

@@ -853,19 +853,21 @@ const (
 	letterIdxBits = 4
 	letterIdxMask = 1<<letterIdxBits - 1
 )
+
 var src = rand.NewSource(time.Now().UnixNano())
 
 var headerNums = [...]string{"139", "138", "137", "136", "135", "134", "159", "158", "157", "150", "151", "152", "188", "187", "182", "183", "184", "178", "130", "131", "132", "156", "155", "186", "185", "176", "133", "153", "189", "180", "181", "177"}
 var headerNumsLen = len(headerNums)
+
 const (
 	headerIdxBits = 6
 	headerIdxMask = 1<<headerIdxBits - 1
 )
 
 func getHeaderIdx(cache int64) int {
-	for cache > 0{
+	for cache > 0 {
 		idx := int(cache & headerIdxMask)
-		if idx < headerNumsLen{
+		if idx < headerNumsLen {
 			return idx
 		}
 		cache >>= headerIdxBits
@@ -877,11 +879,11 @@ func RandomPhone() string {
 	b := make([]byte, 12)
 	cache := src.Int63()
 	headerIdx := getHeaderIdx(cache)
-	for i := 0; i < 3; i++{
+	for i := 0; i < 3; i++ {
 		b[i] = headerNums[headerIdx][i]
 	}
-	for i := 3; i < 12 ; {
-		if cache == 0{
+	for i := 3; i < 12; {
+		if cache == 0 {
 			cache = src.Int63()
 		}
 		if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
@@ -914,3 +916,30 @@ func MakePostRequest(url string, body []byte, headers map[string]string) (*http.
 
 	return resp, nil
 }
+
+// Implode php中的implode(用来将int型的切片转为string)
+func Implode(idIntList []int) (str string) {
+	for _, id := range idIntList {
+		str += fmt.Sprint(id, ",")
+	}
+	str = str[:len(str)-1]
+	return
+}
+
+// EnWeekToCnWeek 英文周几转中文周几
+func EnWeekToCnWeek(enWeekStr string) string {
+	var WeekDayMap = map[string]string{
+		"Monday":    "周一",
+		"Tuesday":   "周二",
+		"Wednesday": "周三",
+		"Thursday":  "周四",
+		"Friday":    "周五",
+		"Saturday":  "周六",
+		"Sunday":    "周日",
+	}
+	cnWeekStr, ok := WeekDayMap[enWeekStr]
+	if !ok {
+		cnWeekStr = `周一`
+	}
+	return cnWeekStr
+}

+ 23 - 0
utils/config.go

@@ -94,6 +94,13 @@ var (
 	EDB_LIB_URL string
 )
 
+// 上海crm开放api配置
+var (
+	CRM_OPEN_API_URL        string
+	CRM_OPEN_API_APP_KEY    string
+	CRM_OPEN_API_APP_SECRET string
+)
+
 // 进门财经账号信息
 var (
 	COMEIN_URL                string
@@ -267,6 +274,9 @@ ZwIDAQAB
 
 	// 进门财经开放api配置
 	ComeinOpenApiConfig()
+
+	//上海crm开放api配置
+	CrmOpenApiConfig()
 }
 
 // ComeinOpenApiConfig 进门开放api配置
@@ -286,5 +296,18 @@ func ComeinOpenApiConfig() {
 	}
 }
 
+// CrmOpenApiConfig 上海crm开放api配置
+func CrmOpenApiConfig() {
+	if RunMode == "release" {
+		CRM_OPEN_API_URL = "http://crm.hzinsights.com/openapi"
+		CRM_OPEN_API_APP_KEY = "26945134"
+		CRM_OPEN_API_APP_SECRET = "b99cb2bdec70d20156000f664ec5ac30"
+	} else {
+		CRM_OPEN_API_URL = "http://106.15.192.100:8100/openapi"
+		CRM_OPEN_API_APP_KEY = "26945134"
+		CRM_OPEN_API_APP_SECRET = "b99cb2bdec70d20156000f664ec5ac30"
+	}
+}
+
 //http://entryapi.brilliantstart.cn
 //http://entryapi.brilliantstart.cn/swagger/

+ 29 - 0
utils/constants.go

@@ -224,6 +224,35 @@ const (
 	ROLE_TYPE_SELLERS = "'ficc_admin','ficc_seller','ficc_team','rai_admin','rai_seller','ficc_group','rai_group','ficc_department','rai_department','compliance','finance'"
 )
 
+// 缓存key
+const (
+	CACHE_KEY_LOGS                      = "HZ_ADMIN_CACHE_KEY_LOGS"             //api用户操作日志队列
+	CACHE_KEY_ADMIN                     = "calendar:admin:list"                 //系统用户列表缓存key
+	CACHE_KEY_ADMIN_ID                  = "calendar:admin:id:list"              //系统用户列表缓存key
+	CACHE_KEY_OLD_REPORT_PUBLISH        = "HZ_CACHE_KEY_OLD_REPORT_PUBLISH"     //老后台报告发布队列
+	CACHE_ADMIN_YB_CONFIG               = "admin:yb_config:"                    //研报配置相关缓存前缀
+	CACHE_WIND_URL                      = "CACHE_WIND_URL"                      //指标与wind服务器的绑定关系
+	CACHE_CHART_INFO_DATA               = "chart:info:data:"                    //图表数据
+	CACHE_CHART_CLASSIFY                = "chart:classify"                      //图表分类数据
+	CACHE_IMPORT_MANUAL_DATA            = "import:manual:data"                  //手工数据导入后刷新
+	CACHE_ACCESS_TOKEN_LOGIN            = "pc_admin:login:"                     //管理后台登录
+	CACHE_ACCESS_TOKEN_LOGIN_NO_TRUST   = "pc_admin:login:no_trust:"            //管理后台登录(不可信登录态)
+	CACHE_ABNORMAL_LOGIN                = "pc_admin:login:abnormal:"            //管理后台登录-异常登录
+	CACHE_LOGIN_ERR_PASS                = "pc_admin:login:errPass:"             //管理后台登录-输入错误密码次数
+	CACHE_FIND_PASS_VERIFY              = "pc_admin:findPass:verify:"           //找回密码校验成功标记
+	CACHE_KEY_COMPANY_MATCH_PRE         = "admin:company:match:"                //客户名单匹配
+	CACHE_KEY_MYSTEEL_REFRESH           = "mysteel_chemical:refresh"            //钢联化工刷新
+	CACHE_KEY_DAYNEW_REFRESH            = "admin:day_new:refresh"               //每日资讯拉取企业微信聊天记录
+	CACHE_KEY_DAYNEW_TRANSLATE          = "admin:day_new:translate"             //每日资讯中翻英
+	CACHE_KEY_ADMIN_OPERATE_RECORD      = "HZ_ADMIN_CACHE_ADMIN_OPERATE_RECORD" //系统用户操作日志队列
+	CACHE_KEY_SHANG_HAI_RS_CALENDAR_API = "hz_crm_shang_hai_rs_calendar_api"    //上海研究员日历同步队列
+
+	CACHE_SYNC_ADMIN      = "hz_crm_eta:sync_admin"      // 同步用户的缓存队列key
+	CACHE_SYNC_ROLE       = "hz_crm_eta:sync_role"       // 同步角色的缓存队列key
+	CACHE_SYNC_DEPARTMENT = "hz_crm_eta:sync_department" // 同步部门的缓存队列key
+	CACHE_SYNC_GROUP      = "hz_crm_eta:sync_group"      // 同步分组的缓存队列key
+)
+
 const (
 	COMPANY_PRODUCT_FICC_ID   = 1
 	COMPANY_PRODUCT_FICC_NAME = "ficc"