kobe6258 il y a 7 mois
Parent
commit
d164bda621

+ 12 - 0
common/exception/exc_enums.go

@@ -29,6 +29,7 @@ const (
 	SMSCodeAlreadySent
 	SMSCodeAlreadySent
 	SMSCodeExpired
 	SMSCodeExpired
 	SMSCodeError
 	SMSCodeError
+	AnalystNotFound
 )
 )
 
 
 // UserErrCode 用户
 // UserErrCode 用户
@@ -41,6 +42,11 @@ const (
 
 
 	JWTTokenExpired
 	JWTTokenExpired
 	JWTTokenInvalid
 	JWTTokenInvalid
+	NotCurrentUserError
+	FeedBackMsgEmpty
+	FeedBackError
+	IllegalFollowType
+	UserFollowAnalystFailed
 )
 )
 
 
 // WechatErrCode 微信
 // WechatErrCode 微信
@@ -75,6 +81,7 @@ var ErrorMap = map[int]string{
 	SMSCodeAlreadySent:    "当前手机验证码已发送,请稍后再试",
 	SMSCodeAlreadySent:    "当前手机验证码已发送,请稍后再试",
 	SMSCodeExpired:        "验证码已过期",
 	SMSCodeExpired:        "验证码已过期",
 	SMSCodeError:          "验证码错误",
 	SMSCodeError:          "验证码错误",
+	AnalystNotFound:       "研究员不存在",
 	//用户
 	//用户
 	TemplateUserNotFound:     "临时用户记录不存在",
 	TemplateUserNotFound:     "临时用户记录不存在",
 	TemplateUserCreateFailed: "创建临时用户失败",
 	TemplateUserCreateFailed: "创建临时用户失败",
@@ -82,6 +89,11 @@ var ErrorMap = map[int]string{
 	JWTTokenDecodeFailed:     "token解析失败",
 	JWTTokenDecodeFailed:     "token解析失败",
 	JWTTokenExpired:          "token已过期",
 	JWTTokenExpired:          "token已过期",
 	JWTTokenInvalid:          "token无效",
 	JWTTokenInvalid:          "token无效",
+	NotCurrentUserError:      "用户信息不一致,非当前手机用户操作",
+	FeedBackMsgEmpty:         "反馈信息不能为空",
+	FeedBackError:            "提交反馈信息失败",
+	IllegalFollowType:        "无效的关注类型",
+	UserFollowAnalystFailed:  "关注研究员失败",
 	//微信
 	//微信
 	WeChatServerError:    "微信服务器发生错误",
 	WeChatServerError:    "微信服务器发生错误",
 	WechatUserInfoFailed: "获取微信用户信息失败",
 	WechatUserInfoFailed: "获取微信用户信息失败",

+ 1 - 0
controllers/media/media_controller.go

@@ -0,0 +1 @@
+package media

+ 34 - 3
controllers/report/report_controller.go

@@ -5,8 +5,8 @@ import (
 	"eta_mini_ht_api/common/exception"
 	"eta_mini_ht_api/common/exception"
 	"eta_mini_ht_api/common/utils/page"
 	"eta_mini_ht_api/common/utils/page"
 	"eta_mini_ht_api/controllers"
 	"eta_mini_ht_api/controllers"
-	"eta_mini_ht_api/domian/user"
 	"eta_mini_ht_api/service/report"
 	"eta_mini_ht_api/service/report"
+	"eta_mini_ht_api/service/user"
 	"fmt"
 	"fmt"
 )
 )
 
 
@@ -121,6 +121,37 @@ func (r *ReportController) PublishRanked() {
 	})
 	})
 }
 }
 
 
+// AnalystReportList @Title 获取最新发布的报告列表
+// @Description 获取最新发布的报告列表
+// @Success 200 {object}
+// @router /AnalystReportList [get]
+func (r *ReportController) AnalystReportList(authorId int) {
+	controllers.Wrap(&r.BaseController, func() (result *controllers.WrapData, err error) {
+		result = r.InitWrapData("分页获取研究员报告列表失败")
+		pageReq := page.Page{
+			Current:  r.PageInfo.Current,
+			PageSize: r.PageInfo.PageSize,
+		}
+		if r.PageInfo.LatestId == 0 {
+			pageReq.LatestId = report.GetTotalPageCount()
+			r.PageInfo.LatestId = pageReq.LatestId
+		} else {
+			pageReq.LatestId = r.PageInfo.LatestId
+		}
+		pageReq.Total = pageReq.LatestId
+		list, err := report.GetReportPage(r.PageInfo)
+		if err != nil {
+			r.FailedResult("分页查询报告列表失败", result)
+			return
+		}
+		reports := new(page.PageResult)
+		reports.Data = list
+		reports.Page = pageReq
+		r.SuccessResult("查询报告列表成功", reports, result)
+		return
+	})
+}
+
 // GetPermissions  @Title 获取品种列表
 // GetPermissions  @Title 获取品种列表
 // @Description 获取最新发布的报告列表
 // @Description 获取最新发布的报告列表
 // @Success 200 {object}
 // @Success 200 {object}
@@ -154,8 +185,8 @@ type RecordCountReq struct {
 func (r *ReportController) Count() {
 func (r *ReportController) Count() {
 	controllers.Wrap(&r.BaseController, func() (result *controllers.WrapData, err error) {
 	controllers.Wrap(&r.BaseController, func() (result *controllers.WrapData, err error) {
 		result = r.InitWrapData("研报点击记录失败")
 		result = r.InitWrapData("研报点击记录失败")
-		var userInfo user.UserDTO
-		userInfo = r.Data["user"].(user.UserDTO)
+		var userInfo user.User
+		userInfo = r.Data["user"].(user.User)
 		recordReq := new(RecordCountReq)
 		recordReq := new(RecordCountReq)
 		r.GetPostParams(recordReq)
 		r.GetPostParams(recordReq)
 		record := convertToRecordCount(recordReq)
 		record := convertToRecordCount(recordReq)

+ 149 - 0
controllers/user/user_controller.go

@@ -3,7 +3,10 @@ package user
 import (
 import (
 	"eta_mini_ht_api/common/component/cache"
 	"eta_mini_ht_api/common/component/cache"
 	logger "eta_mini_ht_api/common/component/log"
 	logger "eta_mini_ht_api/common/component/log"
+	"eta_mini_ht_api/common/exception"
+	authUtils "eta_mini_ht_api/common/utils/auth"
 	"eta_mini_ht_api/controllers"
 	"eta_mini_ht_api/controllers"
+	"eta_mini_ht_api/service/user"
 	"fmt"
 	"fmt"
 )
 )
 
 
@@ -31,3 +34,149 @@ func (u *UserController) Get() {
 	fmt.Print("查询用户列表")
 	fmt.Print("查询用户列表")
 	u.ServeJSON()
 	u.ServeJSON()
 }
 }
+
+type FeedbackReq struct {
+	Mobile  string `json:"mobile"`
+	Message string `json:"message"`
+}
+
+type FollowAnalystReq struct {
+	AnalystId  int    `json:"analystId"`
+	FollowType string `json:"followType"`
+	Mobile     string `json:"mobile"`
+}
+
+// Feedback  用户意见反馈
+// @Summary 用户意见反馈
+// @Description 用户意见反馈
+// @Success 200 {object} controllers.BaseResponse
+// @router /feedback [post]
+func (u *UserController) Feedback() {
+	controllers.Wrap(&u.BaseController, func() (result *controllers.WrapData, err error) {
+		result = u.InitWrapData("提交反馈失败")
+		feedback := new(FeedbackReq)
+		u.GetPostParams(feedback)
+		if !authUtils.IsValidMobile(feedback.Mobile) {
+			u.FailedResult("手机号非法", result)
+			err = exception.New(exception.IllegalPhoneNumber)
+			return
+		}
+		var userInfo user.User
+		userInfo = u.Data["user"].(user.User)
+		if userInfo.Mobile != feedback.Mobile {
+			u.FailedResult("非当前用户的手机号提交", result)
+			err = exception.New(exception.NotCurrentUserError)
+			return
+		}
+		if feedback.Message == "" {
+			u.FailedResult("反馈信息不能为空", result)
+			err = exception.New(exception.FeedBackMsgEmpty)
+			return
+		}
+		err = user.FeedBack(userInfo.Id, feedback.Mobile, feedback.Message)
+		if err != nil {
+			err = exception.New(exception.FeedBackError)
+			u.FailedResult("提交反馈失败", result)
+			return
+		}
+		u.SuccessResult("提交反馈成功", nil, result)
+		return
+	})
+}
+
+type FollowResp struct {
+	AnalystId    int    `json:"analystId"`
+	FollowedType string `json:"FollowedType"`
+}
+
+// FollowAnalyst  关注研究员
+// @Summary 关注研究员
+// @Description 关注研究员
+// @Success 200 {object} controllers.BaseResponse
+// @router /followAnalyst [post]
+func (u *UserController) FollowAnalyst() {
+	controllers.Wrap(&u.BaseController, func() (result *controllers.WrapData, err error) {
+		result = u.InitWrapData("")
+		followAnalyst := new(FollowAnalystReq)
+		u.GetPostParams(followAnalyst)
+		if !authUtils.IsValidMobile(followAnalyst.Mobile) {
+			u.FailedResult("手机号非法", result)
+			err = exception.New(exception.IllegalPhoneNumber)
+			return
+		}
+		var userInfo user.User
+		userInfo = u.Data["user"].(user.User)
+		if userInfo.Mobile != followAnalyst.Mobile {
+			u.FailedResult("非当前用户的手机号提交", result)
+			err = exception.New(exception.NotCurrentUserError)
+			return
+		}
+		if !checkFollowType(followAnalyst.FollowType) {
+			u.FailedResult("关注状态非法", result)
+			err = exception.New(exception.IllegalFollowType)
+			return
+		}
+		var msg string
+		switch followAnalyst.FollowType {
+		case "following":
+			msg = "关注研究员"
+		case "unfollowed":
+			msg = "取消关注研究员"
+		}
+		err = user.FollowAnalyst(userInfo.Id, followAnalyst.AnalystId, followAnalyst.FollowType)
+		if err != nil {
+			u.FailedResult(msg+"失败", result)
+			return
+		}
+		resp := FollowResp{
+			AnalystId:    followAnalyst.AnalystId,
+			FollowedType: followAnalyst.FollowType,
+		}
+		u.SuccessResult(msg+"成功", resp, result)
+		return
+	})
+}
+
+func checkFollowType(followType string) bool {
+	return followType == "following" || followType == "unfollowed"
+}
+
+// AnalystDetail  研究员详情
+// @Summary 研究员详情
+// @Description 研究员详情
+// @Success 200 {object} controllers.BaseResponse
+// @router /analystDetail [get]
+func (u *UserController) AnalystDetail(analystId int) {
+	controllers.Wrap(&u.BaseController, func() (result *controllers.WrapData, err error) {
+		result = u.InitWrapData("获取研究员详情失败")
+		fmt.Println(analystId)
+		userInfo := u.Data["user"].(user.User)
+		detail, err := user.GetAnalystDetail(userInfo.Id, analystId)
+		if err != nil {
+			u.FailedResult("获取研究员详情失败", result)
+			return
+		}
+		u.SuccessResult("获取研究员详情成功", detail, result)
+		return
+	})
+}
+
+// FollowingAnalysts  关注研究员列表
+// @Summary 研究员详情
+// @Description 研究员详情
+// @Success 200 {object} controllers.BaseResponse
+// @router /followingAnalysts [get]
+func (u *UserController) FollowingAnalysts(analystId int) {
+	controllers.Wrap(&u.BaseController, func() (result *controllers.WrapData, err error) {
+		result = u.InitWrapData("获取研究员详情失败")
+		fmt.Println(analystId)
+		userInfo := u.Data["user"].(user.User)
+		detail, err := user.GetAnalystDetail(userInfo.Id, analystId)
+		if err != nil {
+			u.FailedResult("获取研究员详情失败", result)
+			return
+		}
+		u.SuccessResult("获取研究员详情成功", detail, result)
+		return
+	})
+}

+ 5 - 0
domian/financial_analyst/eta_report_author.go

@@ -0,0 +1,5 @@
+package financial_analyst
+
+func GetETAAuthorList() {
+
+}

+ 53 - 0
domian/financial_analyst/financial_analyst.go

@@ -0,0 +1,53 @@
+package financial_analyst
+
+import (
+	logger "eta_mini_ht_api/common/component/log"
+	financialAnalystDao "eta_mini_ht_api/models/financial_analyst"
+)
+
+type FinancialAnalystDTO struct {
+	ETAId        int
+	Name         string
+	Introduction string
+	Status       bool
+	Deleted      bool
+}
+
+func GetAnalystById(id int) (financialAnalyst FinancialAnalystDTO, err error) {
+	analyst, err := financialAnalystDao.GetAnalystById(id)
+	if err != nil {
+		logger.Error("获取研究研失败: %v", err)
+		return
+	}
+	financialAnalyst = convertToBaseDTO(analyst)
+	return
+}
+func SyncAnalyst(list []FinancialAnalystDTO) (err error) {
+	var financialAnalystList []financialAnalystDao.CrmFinancialAnalyst
+	for _, dto := range list {
+		financialAnalyst := convert(dto)
+		financialAnalystList = append(financialAnalystList, financialAnalyst)
+	}
+	return financialAnalystDao.BatchInsertOrUpdate(financialAnalystList)
+}
+
+func convert(dto FinancialAnalystDTO) (financialAnalyst financialAnalystDao.CrmFinancialAnalyst) {
+	var status financialAnalystDao.AnalystStatus
+	if dto.Status {
+		status = financialAnalystDao.AnalystStatusEnabled
+	} else {
+		status = financialAnalystDao.AnalystStatusDisabled
+	}
+	return financialAnalystDao.CrmFinancialAnalyst{
+		ETAId:   dto.ETAId,
+		Name:    dto.Name,
+		Status:  status,
+		Deleted: dto.Deleted,
+	}
+}
+func convertToBaseDTO(financialAnalyst financialAnalystDao.CrmFinancialAnalyst) (dto FinancialAnalystDTO) {
+	return FinancialAnalystDTO{
+		Name:         financialAnalyst.Name,
+		Introduction: financialAnalyst.Introduction,
+	}
+}

+ 138 - 0
domian/report/eta_report_service.go

@@ -0,0 +1,138 @@
+package report
+
+import (
+	logger "eta_mini_ht_api/common/component/log"
+	"eta_mini_ht_api/models/eta"
+	"fmt"
+	"sync"
+	"time"
+)
+
+const (
+	SmartReport = 2
+)
+
+type ETAReportDTO struct {
+	Title            string             `json:"title"`
+	Abstract         string             `json:"abstract"`
+	Author           string             `json:"author"`
+	PublishTime      string             `json:"publishTime"`
+	Content          string             `json:"content"`
+	ReportLayout     int                `json:"reportLayout"`
+	VideoUrl         string             `json:"videoUrl"`
+	VideoName        string             `json:"videoName"`
+	VideoPlaySeconds string             `json:"videoPlaySeconds"`
+	HeadResource     SmartResourceDTO   `json:"headResource"`
+	EndResource      SmartResourceDTO   `json:"endResource"`
+	HasChapter       bool               `json:"hasChapter"`
+	NeedSplice       bool               `json:"needSplice"`
+	SmartReport      bool               `json:"smartReport"`
+	Chapters         []ReportChapterDTO `json:"chapters"`
+}
+
+type ReportChapterDTO struct {
+	Content          string `json:"content"`
+	VideoURL         string `json:"videoURL"`
+	VideoName        string `json:"videoName"`
+	VideoPlaySeconds string `json:"videoPlaySeconds"`
+	VideoSize        string `json:"videoSize"`
+}
+
+type SmartResourceDTO struct {
+	ImgURL string `json:"imgURL"`
+	Style  string `json:"style"`
+}
+
+func GetETAReport(id int) (detail ETAReportDTO, err error) {
+	report, err := eta.GetETAReportById(id)
+	if err != nil {
+		logger.Error("获取ETA研报信息失败:%v", err)
+	}
+	var wg sync.WaitGroup
+	detail = convertToETAReportDTO(report)
+	errChan := make(chan error, 3)
+	if report.HasChapter {
+		wg.Add(1)
+		go func(detail *ETAReportDTO) {
+			defer wg.Done()
+			list, chapterErr := eta.GetChaptersByReportId(report.ID)
+			if chapterErr != nil {
+				errChan <- fmt.Errorf("获取章节信息失败:%v", chapterErr)
+			}
+			detail.Chapters = convertToReportChapterDTO(list)
+		}(&detail)
+	}
+	if report.ReportLayout == SmartReport {
+		if report.HeadResourceId != 0 {
+			wg.Add(1)
+			go func(id int, detail *ETAReportDTO) {
+				defer wg.Done()
+				resource, resourceErr := eta.GetSmartReportResource(id)
+				if resourceErr != nil {
+					errChan <- fmt.Errorf("获取智能研报资源失败:%v", resourceErr)
+				}
+				detail.HeadResource = convertToSmartResourceDTO(resource)
+			}(report.HeadResourceId, &detail)
+		}
+		if report.EndResourceId != 0 {
+			wg.Add(1)
+			go func(id int, detail *ETAReportDTO) {
+				defer wg.Done()
+				resource, resourceErr := eta.GetSmartReportResource(id)
+				if resourceErr != nil {
+					errChan <- fmt.Errorf("获取智能研报资源失败:%v", resourceErr)
+				}
+				detail.EndResource = convertToSmartResourceDTO(resource)
+			}(report.EndResourceId, &detail)
+		}
+	}
+	wg.Wait()
+	close(errChan)
+	for err = range errChan {
+		logger.Error("%v", err)
+	}
+	return
+}
+func convertToSmartResourceDTO(resource eta.SmartReportResource) (dto SmartResourceDTO) {
+	return SmartResourceDTO{
+		ImgURL: resource.ImgURL,
+		Style:  resource.Style,
+	}
+}
+func convertToETAReportDTO(report eta.ETAReport) (dto ETAReportDTO) {
+	dto = ETAReportDTO{
+		Title:            report.Title,
+		Abstract:         report.Abstract,
+		Author:           report.Author,
+		PublishTime:      report.PublishTime,
+		Content:          report.Content,
+		ReportLayout:     report.ReportLayout,
+		VideoUrl:         report.VideoUrl,
+		VideoName:        report.VideoName,
+		VideoPlaySeconds: report.VideoPlaySeconds,
+		HasChapter:       report.HasChapter,
+		NeedSplice:       report.NeedSplice,
+		SmartReport:      report.ReportLayout == SmartReport,
+	}
+	if dto.SmartReport {
+		dto.Content = ""
+	}
+	publishDate, err := time.Parse(time.DateTime, dto.PublishTime)
+	if err == nil {
+		dto.PublishTime = publishDate.Format(time.DateOnly)
+	}
+	return
+}
+func convertToReportChapterDTO(chapters []eta.ReportChapter) (dtoList []ReportChapterDTO) {
+	for _, chapter := range chapters {
+		dto := ReportChapterDTO{
+			Content:          chapter.Content,
+			VideoURL:         chapter.VideoURL,
+			VideoName:        chapter.VideoName,
+			VideoPlaySeconds: chapter.VideoPlaySeconds,
+			VideoSize:        chapter.VideoSize,
+		}
+		dtoList = append(dtoList, dto)
+	}
+	return
+}

+ 20 - 9
domian/report/report_service.go

@@ -47,15 +47,19 @@ type ESReport struct {
 }
 }
 
 
 type ReportDTO struct {
 type ReportDTO struct {
-	ReportID        int         `json:"report_id"`
-	OrgId           int         `json:"org_id"`
-	Title           string      `json:"title"`
-	Author          string      `json:"author"`
-	Source          string      `json:"source"`
-	Abstract        string      `json:"abstract"`
-	PublishedTime   string      `json:"published_time"`
-	PermissionNames interface{} `json:"permissionNames,omitempty"`
-	Highlight       []string    `json:"highlight,omitempty"`
+	ReportID        int             `json:"report_id"`
+	OrgId           int             `json:"org_id"`
+	Title           string          `json:"title"`
+	Author          string          `json:"author"`
+	Source          string          `json:"source"`
+	Abstract        string          `json:"abstract"`
+	PublishedTime   string          `json:"published_time"`
+	PermissionNames interface{}     `json:"permissionNames,omitempty"`
+	Highlight       []string        `json:"highlight,omitempty"`
+	Detail          json.RawMessage `json:"detail,omitempty"`
+}
+
+type Detail struct {
 }
 }
 
 
 type PermissionDTO struct {
 type PermissionDTO struct {
@@ -74,6 +78,13 @@ type RecordCountDTO struct {
 	Additional string
 	Additional string
 }
 }
 
 
+func GetGetReportById(reportId int) (ReportDTO ReportDTO, err error) {
+	report, err := reportDao.GetReportById(reportId)
+	if err == nil {
+		ReportDTO = convertReportDTO(report)
+	}
+	return
+}
 func GetTotalPageCount() (total int64) {
 func GetTotalPageCount() (total int64) {
 	return reportDao.GetTotalPageCount()
 	return reportDao.GetTotalPageCount()
 }
 }

+ 1 - 1
domian/report/user_report_click_flow_service.go

@@ -2,7 +2,7 @@ package report
 
 
 import (
 import (
 	logger "eta_mini_ht_api/common/component/log"
 	logger "eta_mini_ht_api/common/component/log"
-	reportDao "eta_mini_ht_api/models/report"
+	reportDao "eta_mini_ht_api/models/user"
 )
 )
 
 
 func CountReport(record RecordCountDTO) (err error) {
 func CountReport(record RecordCountDTO) (err error) {

+ 58 - 7
domian/user/user_serivce.go

@@ -3,8 +3,9 @@ package user
 import (
 import (
 	"errors"
 	"errors"
 	logger "eta_mini_ht_api/common/component/log"
 	logger "eta_mini_ht_api/common/component/log"
-	"eta_mini_ht_api/models/user"
+	userDao "eta_mini_ht_api/models/user"
 	"gorm.io/gorm"
 	"gorm.io/gorm"
+	"time"
 )
 )
 
 
 type UserDTO struct {
 type UserDTO struct {
@@ -15,7 +16,19 @@ type UserDTO struct {
 	UnionId  string
 	UnionId  string
 }
 }
 
 
-func convertUserDTO(user user.TemplateUser) UserDTO {
+type FeedbackDTO struct {
+	Mobile  string `json:"mobile"`
+	UserId  int    `json:"user_id"`
+	Message string `json:"message"`
+}
+type FollowDTO struct {
+	UserId      int
+	AnalystId   int
+	AnalystName string
+	FollowType  string
+}
+
+func convertUserDTO(user userDao.TemplateUser) UserDTO {
 	return UserDTO{
 	return UserDTO{
 		Id:       user.Id,
 		Id:       user.Id,
 		Username: user.Username,
 		Username: user.Username,
@@ -24,7 +37,7 @@ func convertUserDTO(user user.TemplateUser) UserDTO {
 }
 }
 func GetUserByMobile(mobile string) (UserDTO, error) {
 func GetUserByMobile(mobile string) (UserDTO, error) {
 	var dto UserDTO
 	var dto UserDTO
-	templateUser, err := user.GetUserByMobile(mobile)
+	templateUser, err := userDao.GetUserByMobile(mobile)
 	if err != nil {
 	if err != nil {
 		if !errors.Is(err, gorm.ErrRecordNotFound) {
 		if !errors.Is(err, gorm.ErrRecordNotFound) {
 			logger.Error("查询用户失败:%v", err)
 			logger.Error("查询用户失败:%v", err)
@@ -37,7 +50,7 @@ func GetUserByMobile(mobile string) (UserDTO, error) {
 
 
 func RegisterTemplateUser(dto *UserDTO) (err error) {
 func RegisterTemplateUser(dto *UserDTO) (err error) {
 	templateUser := convertToTemplateUser(dto)
 	templateUser := convertToTemplateUser(dto)
-	err = user.RegisterTemplateUser(&templateUser)
+	err = userDao.RegisterTemplateUser(&templateUser)
 	if err != nil {
 	if err != nil {
 		logger.Error("创建临时用户失败:%v", err)
 		logger.Error("创建临时用户失败:%v", err)
 		return
 		return
@@ -45,8 +58,25 @@ func RegisterTemplateUser(dto *UserDTO) (err error) {
 	convertToUserDTO(templateUser, dto)
 	convertToUserDTO(templateUser, dto)
 	return
 	return
 }
 }
-func convertToTemplateUser(dto *UserDTO) user.TemplateUser {
-	return user.TemplateUser{
+
+func FeedBack(dto FeedbackDTO) (err error) {
+	feedBackEntity := userDao.FeedBack{
+		Message: dto.Message,
+		Mobile:  dto.Mobile,
+		UserID:  dto.UserId,
+	}
+	return userDao.InsertFeedBack(feedBackEntity)
+}
+
+func GetFollowed(userId int, analystId int) string {
+	return userDao.GetFollowed(userId, analystId)
+}
+func FollowAnalyst(dto FollowDTO) (err error) {
+	follow := convertToCrmFollowingAnalyst(dto)
+	return userDao.FollowAnalyst(follow)
+}
+func convertToTemplateUser(dto *UserDTO) userDao.TemplateUser {
+	return userDao.TemplateUser{
 		Username: dto.Username,
 		Username: dto.Username,
 		Mobile:   dto.Mobile,
 		Mobile:   dto.Mobile,
 		OpenId:   dto.OpenId,
 		OpenId:   dto.OpenId,
@@ -54,10 +84,31 @@ func convertToTemplateUser(dto *UserDTO) user.TemplateUser {
 	}
 	}
 }
 }
 
 
-func convertToUserDTO(templateUser user.TemplateUser, dto *UserDTO) {
+func convertToUserDTO(templateUser userDao.TemplateUser, dto *UserDTO) {
 	dto.Id = templateUser.Id
 	dto.Id = templateUser.Id
 	dto.Username = templateUser.Username
 	dto.Username = templateUser.Username
 	dto.Mobile = templateUser.Mobile
 	dto.Mobile = templateUser.Mobile
 	dto.OpenId = templateUser.OpenId
 	dto.OpenId = templateUser.OpenId
 	dto.UnionId = templateUser.UnionId
 	dto.UnionId = templateUser.UnionId
 }
 }
+
+func convertToCrmFollowingAnalyst(dto FollowDTO) userDao.UserAnalystFollowList {
+	return userDao.UserAnalystFollowList{
+		FinancialAnalystID:   dto.AnalystId,
+		FinancialAnalystName: dto.AnalystName,
+		FollowedTime:         time.Now(),
+		Followed:             followType(dto.FollowType),
+		UserID:               dto.UserId,
+	}
+}
+
+func followType(followType string) userDao.FollowingType {
+	switch followType {
+	case "following":
+		return userDao.Following
+	case "unfollowed":
+		return userDao.Unfollowed
+	default:
+		return ""
+	}
+}

+ 4 - 4
middleware/auth_middleware.go

@@ -8,7 +8,7 @@ import (
 	"eta_mini_ht_api/common/utils/redis"
 	"eta_mini_ht_api/common/utils/redis"
 	stringUtils "eta_mini_ht_api/common/utils/string"
 	stringUtils "eta_mini_ht_api/common/utils/string"
 	"eta_mini_ht_api/controllers"
 	"eta_mini_ht_api/controllers"
-	"eta_mini_ht_api/domian/user"
+	"eta_mini_ht_api/service/user"
 	"github.com/beego/beego/v2/server/web"
 	"github.com/beego/beego/v2/server/web"
 	"github.com/beego/beego/v2/server/web/context"
 	"github.com/beego/beego/v2/server/web/context"
 	"strings"
 	"strings"
@@ -71,14 +71,14 @@ func AuthMiddleware() web.FilterFunc {
 				return
 				return
 			}
 			}
 			//组装用户信息
 			//组装用户信息
-			var userDTO user.UserDTO
-			userDTO, err = user.GetUserByMobile(info.Mobile)
+			var userInfo user.User
+			userInfo, err = user.GetUserByMobile(info.Mobile)
 			if err != nil {
 			if err != nil {
 				logger.Error("获取用户信息失败:%v", err)
 				logger.Error("获取用户信息失败:%v", err)
 				_ = ctx.JSONResp(illegalUser())
 				_ = ctx.JSONResp(illegalUser())
 				return
 				return
 			}
 			}
-			ctx.Input.SetData("user", userDTO)
+			ctx.Input.SetData("user", userInfo)
 			return
 			return
 		}
 		}
 		return
 		return

+ 23 - 0
models/eta/eta_author.go

@@ -0,0 +1,23 @@
+package eta
+
+import (
+	"eta_mini_ht_api/models"
+)
+
+type ReportAuthor struct {
+	Id           int    `gorm:"primaryKey;autoIncrement;column:id;comment:主键"`
+	AuthorType   int    `gorm:"default:1;comment:'类型,1:中文;2:英文'"`
+	ReportAuthor string `gorm:"type:varchar(50);default:''"`
+	Enable       bool   `gorm:"default:1;comment:'是否启用,0:禁用,1:启用'"`
+	IsDelete     bool   `gorm:"default:0;comment:'是否删除,0:未删除,1:已删除'"`
+}
+
+func (ra ReportAuthor) TableName() string {
+	return "report_author"
+}
+
+func GetReportAuthorList() (authors []ReportAuthor, err error) {
+	db := models.ETA()
+	err = db.Model(&ReportAuthor{}).Where("author_type = ?", 1).Find(&authors).Error
+	return
+}

+ 29 - 0
models/eta/eta_chapter.go

@@ -0,0 +1,29 @@
+package eta
+
+import "eta_mini_ht_api/models"
+
+const (
+	columns = "content,video_url,video_name,video_play_seconds,video_size"
+)
+
+// ReportChapter 表示报告章节的结构体
+type ReportChapter struct {
+	Title            string `gorm:"column:title;size:125;not null;default:'';comment:'章节标题'"`
+	Abstract         string `gorm:"column:abstract;size:255;not null;default:'';comment:'摘要'"`
+	Author           string `gorm:"column:author;size:50;not null;default:'';comment:'作者'"`
+	Content          string `gorm:"column:content;type:longtext;comment:'内容'"`
+	VideoURL         string `gorm:"column:video_url;size:255;not null;default:'';comment:'音频文件URL'"`
+	VideoName        string `gorm:"column:video_name;size:255;not null;default:'';comment:'音频文件名称'"`
+	VideoPlaySeconds string `gorm:"column:video_play_seconds;size:255;not null;default:'';comment:'音频播放时长'"`
+	VideoSize        string `gorm:"column:video_size;size:20;not null;default:'';comment:'音频文件大小,单位M'"`
+}
+
+func GetChaptersByReportId(reportId int) (chapters []ReportChapter, err error) {
+	db := models.ETA()
+	err = db.Select(columns).Where("report_id = ?", reportId).Where("publish_state = ?", 2).Find(&chapters).Error
+	return
+}
+
+func (ReportChapter) TableName() string {
+	return "report_chapter"
+}

+ 27 - 12
models/eta/eta_report.go

@@ -6,9 +6,10 @@ import (
 )
 )
 
 
 const (
 const (
-	colunms   = "id,author,abstract,title,publish_time,"
-	published = 2
-	passed    = 6
+	colunms      = "id,author,abstract,title,publish_time,"
+	detailColumn = "id,author,abstract,title,publish_time,content,collaborate_type,report_layout,video_url,video_name,video_play_seconds,head_resource_id,end_resource_id,has_chapter,need_splice"
+	published    = 2
+	passed       = 6
 
 
 	limit                                  = 50
 	limit                                  = 50
 	TlbChartPermissionSearchKeyWordMapping = "chart_permission_search_key_word_mapping"
 	TlbChartPermissionSearchKeyWordMapping = "chart_permission_search_key_word_mapping"
@@ -23,15 +24,25 @@ func (ETAReport) TableName() string {
 }
 }
 
 
 type ETAReport struct {
 type ETAReport struct {
-	ID               int    `gorm:"primary_key;auto_increment" json:"id"`
-	ClassifyID       int    `gorm:"_" json:"classify_id"`
-	ClassifyIDFirst  int    `gorm:"column:classify_id_first;default:0" json:"classify_id_first"`
-	ClassifyIDSecond int    `gorm:"column:classify_id_second;default:0" json:"classify_id_second"`
-	ClassifyIDThird  int    `gorm:"column:classify_id_third;default:0" json:"classify_id_third"`
-	Title            string `gorm:"column:title;size:125;" json:"title"`
-	Abstract         string `gorm:"column:abstract;size:255;" json:"abstract"`
-	Author           string `gorm:"column:author;size:50;" json:"author"`
-	PublishTime      string `gorm:"column:publish_time" json:"publish_time"`
+	ID               int    `gorm:"primary_key;auto_increment"`
+	ClassifyID       int    `gorm:"_"`
+	ClassifyIDFirst  int    `gorm:"column:classify_id_first;default:0"`
+	ClassifyIDSecond int    `gorm:"column:classify_id_second;default:0"`
+	ClassifyIDThird  int    `gorm:"column:classify_id_third;default:0"`
+	Title            string `gorm:"column:title;size:125;"`
+	Abstract         string `gorm:"column:abstract;size:255;"`
+	Author           string `gorm:"column:author;size:50;"`
+	PublishTime      string `gorm:"column:publish_time"`
+	Content          string `gorm:"column:content"`
+	CollaborateType  int    `gorm:"column:collaborate_type"`
+	ReportLayout     int    `gorm:"column:report_layout"`
+	VideoUrl         string `gorm:"column:video_url"`
+	VideoName        string `gorm:"column:video_name"`
+	VideoPlaySeconds string `gorm:"column:video_play_seconds"`
+	HeadResourceId   int    `gorm:"column:head_resource_id"`
+	EndResourceId    int    `gorm:"column:end_resource_id"`
+	HasChapter       bool   `gorm:"column:has_chapter"`
+	NeedSplice       bool   `gorm:"column:need_splice"`
 }
 }
 
 
 //type ReportClassify struct {
 //type ReportClassify struct {
@@ -54,6 +65,10 @@ func GetETAReports(id int) (reports []ETAReport, err error) {
 	return
 	return
 }
 }
 
 
+func GetETAReportById(id int) (report ETAReport, err error) {
+	err = models.ETA().Table("report").Select(detailColumn).Where("id = ?", id).Where("state =? or state=?", published, passed).First(&report).Error
+	return
+}
 func DoSql(sql string, result interface{}, values ...interface{}) (err error) {
 func DoSql(sql string, result interface{}, values ...interface{}) (err error) {
 	db := models.ETA()
 	db := models.ETA()
 	return db.Raw(sql, values...).Scan(&result).Error
 	return db.Raw(sql, values...).Scan(&result).Error

+ 23 - 0
models/eta/smart_report_source.go

@@ -0,0 +1,23 @@
+package eta
+
+import "eta_mini_ht_api/models"
+
+type SmartReportResource struct {
+	ImgURL string `gorm:"column:img_url;type:longtext;character_set:utf8mb4;comment:'图片链接'"`
+	Style  string `gorm:"column:style;type:longtext;character_set:utf8mb4;comment:'版图样式'"`
+}
+
+const (
+	column = "img_url,style"
+)
+
+// TableName 设置表名
+func (SmartReportResource) TableName() string {
+	return "smart_report_resource"
+}
+
+func GetSmartReportResource(id int) (resource SmartReportResource, err error) {
+	db := models.ETA()
+	err = db.Select(column).Where("resource_id = ?", id).First(&resource).Error
+	return
+}

+ 52 - 0
models/financial_analyst/financial_analyst.go

@@ -0,0 +1,52 @@
+package financial_analyst
+
+import (
+	"eta_mini_ht_api/models"
+	"gorm.io/gorm"
+	"gorm.io/gorm/clause"
+	"time"
+)
+
+type AnalystStatus string
+
+const (
+	AnalystStatusEnabled  AnalystStatus = "enabled"
+	AnalystStatusDisabled AnalystStatus = "disabled"
+	columns                             = "id,name,introduction"
+)
+
+type CrmFinancialAnalyst struct {
+	Id           int           `gorm:"primaryKey;autoIncrement;column:id;comment:主键"`
+	ETAId        int           `gorm:"column:eta_id"`
+	HTId         int           `gorm:"column:ht_id"`
+	Name         string        `gorm:"column:name"`
+	HeadImgURL   string        `gorm:"column:head_img_url"`
+	Introduction string        `gorm:"column:introduction"`
+	Status       AnalystStatus `gorm:"column:status"`
+	Deleted      bool          `gorm:"column:deleted"`
+	CreatedTime  time.Time     `gorm:"column:created_time;type:timestamps;comment:创建时间"`
+	UpdatedTime  time.Time     `gorm:"column:updated_time;type:timestamps;comment:更新时间"`
+}
+
+func BatchInsertOrUpdate(list []CrmFinancialAnalyst) (err error) {
+	db := models.Main()
+	OnConflictFunc := clause.OnConflict{
+		Columns:   []clause.Column{{Name: "eta_id"}},
+		DoUpdates: clause.AssignmentColumns([]string{"name", "status", "deleted"}),
+	}
+
+	// 执行批量插入或更新操作
+	err = db.Clauses(OnConflictFunc).Create(&list).Error
+	return
+}
+
+func (fa *CrmFinancialAnalyst) BeforeCreate(db *gorm.DB) (err error) {
+	fa.CreatedTime = time.Now()
+	return
+}
+
+func GetAnalystById(id int) (analyst CrmFinancialAnalyst, err error) {
+	db := models.Main()
+	err = db.Select(columns).Where("id = ?", id).First(&analyst).Error
+	return
+}

+ 10 - 0
models/report/report.go

@@ -52,6 +52,15 @@ func (t *Report) BeforeCreate(_ *gorm.DB) (err error) {
 	t.CreatedTime = time.Now()
 	t.CreatedTime = time.Now()
 	return
 	return
 }
 }
+
+func GetReportById(reportId int) (report Report, err error) {
+	db := models.Main()
+	err = db.Select(CommonColumns).Where("id = ?", reportId).First(&report).Error
+	if err != nil {
+		logger.Error("查询报告失败:%v", err)
+	}
+	return
+}
 func GetETALatestReportId() (id int, err error) {
 func GetETALatestReportId() (id int, err error) {
 	sql := "select IFNULL(max(org_id),0)  from reports where source = ?"
 	sql := "select IFNULL(max(org_id),0)  from reports where source = ?"
 	err = DoSql(sql, &id, SourceETA)
 	err = DoSql(sql, &id, SourceETA)
@@ -93,6 +102,7 @@ func GetListByCondition[T any](column string, values []T) (reports []Report, err
 	}
 	}
 	return
 	return
 }
 }
+
 func GetTotalPageCount() (total int64) {
 func GetTotalPageCount() (total int64) {
 	db := models.Main()
 	db := models.Main()
 	err := db.Model(&Report{}).Count(&total).Error
 	err := db.Model(&Report{}).Count(&total).Error

+ 25 - 0
models/user/feek_back.go

@@ -0,0 +1,25 @@
+package user
+
+import (
+	"eta_mini_ht_api/models"
+	"gorm.io/gorm"
+	"time"
+)
+
+type FeedBack struct {
+	Id          int    `gorm:" primaryKey;autoIncrement:'id'"`
+	UserID      int    `gorm:"index"`
+	Mobile      string `gorm:"size:15"`
+	Message     string `gorm:"type:longtext"`
+	CreatedTime time.Time
+}
+
+func InsertFeedBack(feedBack FeedBack) (err error) {
+	db := models.Main()
+	return db.Create(&feedBack).Error
+}
+
+func (fb *FeedBack) BeforeCreate(tx *gorm.DB) (err error) {
+	fb.CreatedTime = time.Now()
+	return
+}

+ 79 - 0
models/user/user_analyst_follow_list.go

@@ -0,0 +1,79 @@
+package user
+
+import (
+	"errors"
+	logger "eta_mini_ht_api/common/component/log"
+	"eta_mini_ht_api/models"
+	"gorm.io/gorm"
+	"time"
+)
+
+type FollowingType string
+
+const (
+	Following  FollowingType = "following"
+	Unfollowed FollowingType = "unfollowed"
+)
+
+// UserAnalystFollowList 代表用户关注的研究员列表
+type UserAnalystFollowList struct {
+	ID                   int           `gorm:"primary_key;auto_increment;column:id"`
+	UserID               int           `gorm:"not null;index:user_id"`
+	FinancialAnalystID   int           `gorm:"not null;index:financial_analyst_id"`
+	FinancialAnalystName string        `gorm:"not null;size:100"`
+	Followed             FollowingType `gorm:"type:enum('following', 'unfollowed');not null"`
+	FollowedTime         time.Time     `gorm:"type:timestamp;not null;default:CURRENT_TIMESTAMP;onupdate:CURRENT_TIMESTAMP"`
+	CreatedTime          time.Time     `gorm:"type:timestamp;not null;default:0000-00-00 00:00:00"`
+	UpdatedTime          time.Time     `gorm:"type:timestamp;null;onupdate:CURRENT_TIMESTAMP"`
+}
+
+func (fa UserAnalystFollowList) TableName() string {
+	return "user_analyst_follow_list"
+}
+func (fa *UserAnalystFollowList) BeforeCreate(_ *gorm.DB) (err error) {
+	fa.CreatedTime = time.Now()
+	return
+}
+func FollowAnalyst(follow UserAnalystFollowList) (err error) {
+	if follow.Followed == "" {
+		err = errors.New("关注状态非法")
+		return
+	}
+	db := models.Main()
+	var dbFollow UserAnalystFollowList
+	err = db.Model(&UserAnalystFollowList{}).Where("user_id = ? and financial_analyst_id = ?", follow.UserID, follow.FinancialAnalystID).First(&dbFollow).Error
+	if err != nil {
+		if !errors.Is(err, gorm.ErrRecordNotFound) {
+			return
+		}
+		if follow.Followed == Unfollowed {
+			return nil
+		}
+		err = db.Create(&follow).Error
+		return
+	}
+	if follow.Followed == dbFollow.Followed {
+		return
+	}
+	follow.ID = dbFollow.ID
+	follow.CreatedTime = dbFollow.CreatedTime
+	follow.FollowedTime = dbFollow.FollowedTime
+	if follow.Followed == Following {
+		follow.FollowedTime = time.Now()
+	}
+	err = db.Updates(&follow).Error
+	return
+}
+
+func GetFollowed(userId int, analystId int) string {
+	db := models.Main()
+	var record UserAnalystFollowList
+	err := db.Model(&UserAnalystFollowList{}).Where("user_id = ? and financial_analyst_id = ?", userId, analystId).Where("followed = ?", Following).First(&record).Error
+	if err != nil {
+		if !errors.Is(err, gorm.ErrRecordNotFound) {
+			logger.Error("查询关注信息失败:%v", err)
+		}
+		return string(Unfollowed)
+	}
+	return string(record.Followed)
+}

+ 1 - 1
models/report/user_report_click_flow.go → models/user/user_report_click_flow.go

@@ -1,4 +1,4 @@
-package report
+package user
 
 
 import (
 import (
 	"eta_mini_ht_api/models"
 	"eta_mini_ht_api/models"

+ 40 - 0
routers/commentsRouter.go

@@ -7,6 +7,17 @@ import (
 
 
 func init() {
 func init() {
 
 
+    beego.GlobalControllerRouter["eta_mini_ht_api/controllers/report:ReportController"] = append(beego.GlobalControllerRouter["eta_mini_ht_api/controllers/report:ReportController"],
+        beego.ControllerComments{
+            Method: "AnalystReportList",
+            Router: `/AnalystReportList`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(
+				param.New("authorId"),
+			),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta_mini_ht_api/controllers/report:ReportController"] = append(beego.GlobalControllerRouter["eta_mini_ht_api/controllers/report:ReportController"],
     beego.GlobalControllerRouter["eta_mini_ht_api/controllers/report:ReportController"] = append(beego.GlobalControllerRouter["eta_mini_ht_api/controllers/report:ReportController"],
         beego.ControllerComments{
         beego.ControllerComments{
             Method: "Count",
             Method: "Count",
@@ -101,4 +112,33 @@ func init() {
             Filters: nil,
             Filters: nil,
             Params: nil})
             Params: nil})
 
 
+    beego.GlobalControllerRouter["eta_mini_ht_api/controllers/user:UserController"] = append(beego.GlobalControllerRouter["eta_mini_ht_api/controllers/user:UserController"],
+        beego.ControllerComments{
+            Method: "AnalystDetail",
+            Router: `/analystDetail`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(
+				param.New("analystId"),
+			),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta_mini_ht_api/controllers/user:UserController"] = append(beego.GlobalControllerRouter["eta_mini_ht_api/controllers/user:UserController"],
+        beego.ControllerComments{
+            Method: "Feedback",
+            Router: `/feedback`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta_mini_ht_api/controllers/user:UserController"] = append(beego.GlobalControllerRouter["eta_mini_ht_api/controllers/user:UserController"],
+        beego.ControllerComments{
+            Method: "FollowAnalyst",
+            Router: `/followAnalyst`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
 }
 }

+ 81 - 14
service/report/report_service.go

@@ -1,6 +1,7 @@
 package report
 package report
 
 
 import (
 import (
+	"encoding/json"
 	logger "eta_mini_ht_api/common/component/log"
 	logger "eta_mini_ht_api/common/component/log"
 	"eta_mini_ht_api/common/exception"
 	"eta_mini_ht_api/common/exception"
 	"eta_mini_ht_api/common/utils/date"
 	"eta_mini_ht_api/common/utils/date"
@@ -10,6 +11,11 @@ import (
 	"time"
 	"time"
 )
 )
 
 
+const (
+	SourceETA = "ETA"
+	SourceHT  = "HT"
+)
+
 type PublishRankedReport struct {
 type PublishRankedReport struct {
 	Id              int
 	Id              int
 	OrgId           int
 	OrgId           int
@@ -20,11 +26,12 @@ type PublishRankedReport struct {
 }
 }
 
 
 type HotRankedReport struct {
 type HotRankedReport struct {
-	Id            int
-	OrgId         int
-	Count         int
-	Title         string
-	PublishedTime string
+	Id              int
+	OrgId           int
+	Count           int
+	Title           string
+	PublishedTime   string
+	PermissionNames interface{}
 }
 }
 
 
 type PermissionNode struct {
 type PermissionNode struct {
@@ -45,6 +52,41 @@ type RecordCount struct {
 }
 }
 
 
 func GetReportById(reportId int) (report reportService.ReportDTO, err error) {
 func GetReportById(reportId int) (report reportService.ReportDTO, err error) {
+	report, err = reportService.GetGetReportById(reportId)
+	if err != nil {
+		logger.Error("获取研报失败:%v", err)
+		err = exception.New(exception.GetReportFailed)
+		return
+	}
+	var detail interface{}
+	switch report.Source {
+	case SourceETA:
+		detail, err = getETAReportDetail(&report)
+		if err != nil {
+			return reportService.ReportDTO{}, err
+		}
+	case SourceHT:
+		detail = nil
+		return
+	}
+	if err != nil {
+		logger.Error("获取研报详情失败失败:%v")
+		err = exception.New(exception.GetReportFailed)
+		return
+	}
+	json, err := json.Marshal(detail)
+	if err != nil {
+		logger.Error("生成研报详情失败:%v", err)
+		err = exception.New(exception.GetReportFailed)
+	}
+	report.Detail = json
+	return
+}
+
+func getETAReportDetail(report *reportService.ReportDTO) (etaReport reportService.ETAReportDTO, err error) {
+	return reportService.GetETAReport(report.OrgId)
+}
+func getHTReportDetail(report *reportService.ReportDTO) (err error) {
 	return
 	return
 }
 }
 func GetTotalPageCount() (total int64) {
 func GetTotalPageCount() (total int64) {
@@ -53,6 +95,15 @@ func GetTotalPageCount() (total int64) {
 func SearchReportList(key string, pageInfo page.PageInfo) (reports []reportService.ReportDTO, err error) {
 func SearchReportList(key string, pageInfo page.PageInfo) (reports []reportService.ReportDTO, err error) {
 	offset := page.StartIndex(pageInfo.Current, pageInfo.PageSize)
 	offset := page.StartIndex(pageInfo.Current, pageInfo.PageSize)
 	reports, err = reportService.SearchReportList(key, offset, pageInfo.PageSize, pageInfo.LatestId)
 	reports, err = reportService.SearchReportList(key, offset, pageInfo.PageSize, pageInfo.LatestId)
+	var wg sync.WaitGroup
+	wg.Add(len(reports))
+	for i := 0; i < len(reports); i++ {
+		go func(report *reportService.ReportDTO) {
+			defer wg.Done()
+			report.PermissionNames = getReportPermissionNames(report.OrgId, report.Source)
+		}(&reports[i])
+	}
+	wg.Wait()
 	if err != nil {
 	if err != nil {
 		err = exception.New(exception.SearchReportPageFailed)
 		err = exception.New(exception.SearchReportPageFailed)
 	}
 	}
@@ -66,6 +117,16 @@ func SearchMaxReportId() (id int64) {
 // GetReportPage 分页获取报告列表
 // GetReportPage 分页获取报告列表
 func GetReportPage(pageInfo page.PageInfo) (list []reportService.ReportDTO, err error) {
 func GetReportPage(pageInfo page.PageInfo) (list []reportService.ReportDTO, err error) {
 	list, err = reportService.GetReportPage(pageInfo)
 	list, err = reportService.GetReportPage(pageInfo)
+	//并发获取研报的标签
+	var wg sync.WaitGroup
+	wg.Add(len(list))
+	for i := 0; i < len(list); i++ {
+		go func(report *reportService.ReportDTO) {
+			defer wg.Done()
+			report.PermissionNames = getReportPermissionNames(report.OrgId, report.Source)
+		}(&list[i])
+	}
+	wg.Wait()
 	if err != nil {
 	if err != nil {
 		err = exception.New(exception.QueryReportPageFailed)
 		err = exception.New(exception.QueryReportPageFailed)
 	}
 	}
@@ -91,6 +152,15 @@ func GetRandedReportByWeeklyHot(limit int) (reports []HotRankedReport, err error
 			err = exception.New(exception.GetHotRandListFailed)
 			err = exception.New(exception.GetHotRandListFailed)
 			return
 			return
 		}
 		}
+		var wg sync.WaitGroup
+		wg.Add(len(dtoList))
+		for i := 0; i < len(dtoList); i++ {
+			go func(report *reportService.ReportDTO) {
+				defer wg.Done()
+				report.PermissionNames = getReportPermissionNames(report.OrgId, report.Source)
+			}(&dtoList[i])
+		}
+		wg.Wait()
 		reports = make([]HotRankedReport, len(ids))
 		reports = make([]HotRankedReport, len(ids))
 		for i := 0; i < len(dtoList); i++ {
 		for i := 0; i < len(dtoList); i++ {
 			report := convertToHotRankedReport(dtoList[i])
 			report := convertToHotRankedReport(dtoList[i])
@@ -168,15 +238,12 @@ func assemblePermissionNode(list []reportService.PermissionDTO, node *Permission
 }
 }
 func convertToHotRankedReport(dto reportService.ReportDTO) (report HotRankedReport) {
 func convertToHotRankedReport(dto reportService.ReportDTO) (report HotRankedReport) {
 	report = HotRankedReport{
 	report = HotRankedReport{
-		Id:            dto.ReportID,
-		OrgId:         dto.OrgId,
-		PublishedTime: dto.PublishedTime,
-		Title:         dto.Title,
-	}
-	//publishDate, err := time.Parse(time.DateTime, report.PublishedTime)
-	//if err == nil {
-	//	report.PublishedTime = publishDate.Format(time.DateOnly)
-	//}
+		Id:              dto.ReportID,
+		OrgId:           dto.OrgId,
+		PublishedTime:   dto.PublishedTime,
+		Title:           dto.Title,
+		PermissionNames: dto.PermissionNames,
+	}
 	return
 	return
 }
 }
 func convertToPublishRankedReportList(dtoList []reportService.ReportDTO) (reports []PublishRankedReport) {
 func convertToPublishRankedReportList(dtoList []reportService.ReportDTO) (reports []PublishRankedReport) {

+ 88 - 0
service/user/user_service.go

@@ -0,0 +1,88 @@
+package user
+
+import (
+	logger "eta_mini_ht_api/common/component/log"
+	"eta_mini_ht_api/common/exception"
+	analystService "eta_mini_ht_api/domian/financial_analyst"
+	userService "eta_mini_ht_api/domian/user"
+)
+
+type User struct {
+	Id       int
+	Username string
+	Mobile   string
+	OpenId   string
+}
+
+type AnalystDetail struct {
+	AnalystName  string `json:"AnalystName"`
+	Introduction string `json:"Introduction"`
+	Followed     string `json:"Followed"`
+}
+
+func GetAnalystDetail(userId int, analystId int) (analystDetail AnalystDetail, err error) {
+	analyst, err := analystService.GetAnalystById(analystId)
+	if err != nil {
+		logger.Error("研究员信息不存在:%v", err)
+		err = exception.New(exception.AnalystNotFound)
+	}
+	analystDetail = convertToAnalystDetail(analyst)
+	//研究员关注状态
+	analystDetail.Followed = userService.GetFollowed(userId, analystId)
+	return
+
+}
+
+func convertToAnalystDetail(dto analystService.FinancialAnalystDTO) AnalystDetail {
+	return AnalystDetail{
+		AnalystName:  dto.Name,
+		Introduction: dto.Introduction,
+	}
+
+}
+func FollowAnalyst(userId int, analystId int, followType string) (err error) {
+	FinancialAnalystDTO, err := analystService.GetAnalystById(analystId)
+	if err != nil {
+		err = exception.New(exception.AnalystNotFound)
+	}
+	followDTO := userService.FollowDTO{
+		UserId:      userId,
+		AnalystId:   analystId,
+		AnalystName: FinancialAnalystDTO.Name,
+		FollowType:  followType,
+	}
+	err = userService.FollowAnalyst(followDTO)
+	if err != nil {
+		logger.Error("关注研究员失败:%v", err)
+		err = exception.New(exception.UserFollowAnalystFailed)
+	}
+	return
+}
+func FeedBack(userId int, mobile string, message string) (err error) {
+	feedback := userService.FeedbackDTO{
+		UserId:  userId,
+		Mobile:  mobile,
+		Message: message,
+	}
+	err = userService.FeedBack(feedback)
+	if err != nil {
+		err = exception.New(exception.FeedBackError)
+	}
+	return
+}
+
+func GetUserByMobile(mobile string) (user User, err error) {
+	userDTO, err := userService.GetUserByMobile(mobile)
+	if err == nil {
+		user = convertToUser(userDTO)
+	}
+	return
+}
+func convertToUser(userDTO userService.UserDTO) User {
+	return User{
+		Id:       userDTO.Id,
+		Username: userDTO.Username,
+		Mobile:   userDTO.Mobile,
+		OpenId:   userDTO.OpenId,
+	}
+}

+ 60 - 0
task/eta/author/eta_author_task.go

@@ -0,0 +1,60 @@
+package author
+
+import (
+	"encoding/json"
+	logger "eta_mini_ht_api/common/component/log"
+	"eta_mini_ht_api/common/contants"
+	"eta_mini_ht_api/domian/financial_analyst"
+	"eta_mini_ht_api/models/eta"
+	"eta_mini_ht_api/task/base"
+)
+
+var (
+	taskName base.TaskType = "ETAAuthorSyncTask"
+	cron                   = "0/60 * * * * *"
+)
+
+// Execute Task ETA取研报的数据
+func (au *AuthorTask) Execute(taskDetail *base.TaskDetail) error {
+	logger.Info(contants.TaskFormat, "同步ETA作者开始")
+	authors, err := eta.GetReportAuthorList()
+	if err != nil {
+		logger.Error("获取ETA作者列表失败:%v", err)
+		return err
+	}
+	if len(authors) > 0 {
+		var list []byte
+		list, err = json.Marshal(authors)
+		if err == nil {
+			taskDetail.Content = string(list)
+		}
+
+		var financialAnalystList []financial_analyst.FinancialAnalystDTO
+		for _, author := range authors {
+			financialAnalystList = append(financialAnalystList, convert(author))
+		}
+		err = financial_analyst.SyncAnalyst(financialAnalystList)
+		if err != nil {
+			logger.Error("同步ETA研报列表失败:%v", err)
+			return err
+		}
+	}
+	logger.Info(contants.TaskFormat, "同步ETA研报库结束")
+	return nil
+}
+
+type AuthorTask struct {
+}
+
+func convert(author eta.ReportAuthor) financial_analyst.FinancialAnalystDTO {
+	return financial_analyst.FinancialAnalystDTO{
+		Deleted: author.IsDelete,
+		ETAId:   author.Id,
+		Name:    author.ReportAuthor,
+		Status:  author.Enable,
+	}
+}
+func init() {
+	authorTask := base.NewTask(taskName, cron, new(AuthorTask), base.DEV)
+	base.RegisterTask(&authorTask)
+}

+ 3 - 3
task/eta/eta_report_task.go → task/eta/report/eta_report_task.go

@@ -1,4 +1,4 @@
-package eta
+package report
 
 
 import (
 import (
 	"encoding/json"
 	"encoding/json"
@@ -11,7 +11,7 @@ import (
 
 
 var (
 var (
 	taskName base.TaskType = "ETAReportSyncTask"
 	taskName base.TaskType = "ETAReportSyncTask"
-	cron                   = "0/10 * * * * *"
+	cron                   = "0/60 * * * * *"
 )
 )
 
 
 // Execute Task ETA取研报的数据
 // Execute Task ETA取研报的数据
@@ -44,6 +44,6 @@ type ReportTask struct {
 }
 }
 
 
 func init() {
 func init() {
-	reportTask := base.NewTask(taskName, cron, new(ReportTask), base.DEV)
+	reportTask := base.NewTask(taskName, cron, new(ReportTask), base.PROD)
 	base.RegisterTask(&reportTask)
 	base.RegisterTask(&reportTask)
 }
 }

+ 2 - 1
task/task_starter.go

@@ -2,7 +2,8 @@ package task
 
 
 import (
 import (
 	"eta_mini_ht_api/task/base"
 	"eta_mini_ht_api/task/base"
-	_ "eta_mini_ht_api/task/eta"
+	_ "eta_mini_ht_api/task/eta/author"
+	_ "eta_mini_ht_api/task/eta/report"
 	_ "eta_mini_ht_api/task/sms"
 	_ "eta_mini_ht_api/task/sms"
 	"github.com/beego/beego/v2/server/web"
 	"github.com/beego/beego/v2/server/web"
 	"github.com/beego/beego/v2/task"
 	"github.com/beego/beego/v2/task"