package article import ( "cls/internal/domain/article" "cls/internal/domain/free_trial" "cls/internal/domain/purchase" domainUser "cls/internal/domain/user" "cls/pkg/logger" "cls/pkg/util/page" "fmt" "strings" "time" "xorm.io/builder" ) type ArticleService struct { repo article.ArticleRepository userRepo domainUser.UserRepository purchaseRepo purchase.Repository userAggregateRepo domainUser.UserAggregateRepository FreeRepo free_trial.FreeTrialRepository resp *ArticleResp log logger.Logger } func NewArticleService(repo article.ArticleRepository, userRepo domainUser.UserRepository, purchaseRepo purchase.Repository, userAggregateRepo domainUser.UserAggregateRepository, FreeRepo free_trial.FreeTrialRepository, resp *ArticleResp, log logger.New) *ArticleService { return &ArticleService{repo, userRepo, purchaseRepo, userAggregateRepo, FreeRepo, resp, log("cls:service:article")} } var class_type = map[uint64]string{ 20014: "狙击龙虎榜", 20015: "盘中宝", 20021: "风口研报", 20022: "公告全知道", 20023: "研选", 20025: "财联社早知道", 20026: "电报解读", 20087: "金牌纪要库", } var class_type_reverse = map[string]uint64{ "狙击龙虎榜": 20014, "盘中宝": 20015, "风口研报": 20021, "公告全知道": 20022, "研选": 20023, "财联社早知道": 20025, "电报解读": 20026, "金牌纪要库": 20087, } func (a *ArticleService) Find(ePhone string, page *page.Page, searchParams map[string]string) error { conds := make([]builder.Cond, 0) class := searchParams["search_like_class"] if class != "" { classCode := class_type_reverse[class] if classCode != 0 { conds = append(conds, builder.Eq{"type": classCode}) } } conds = append(conds, builder.And(builder.Gt{"ctime": time.Now().AddDate(0, 0, -2).Unix()})) articles := make([]*article.LianV1Article, 0) page.Content = &articles err := a.repo.Find(page, conds) if err != nil { a.log.Error(err) return err } purchaseId := make(map[uint64]struct{}) if ePhone != "" { user, err := a.userRepo.FindByPhone(ePhone) if err != nil { a.log.Error(err) } columnData, err := a.purchaseRepo.FindColumnsByUserId(user.Id) columnMap := make(map[uint64]struct{}) if len(columnData) != 0 { for _, v := range columnData { columnMap[v.ContentId] = struct{}{} } } articleIds := make([]uint64, 0, len(articles)) for _, v := range articles { _, has := columnMap[v.Type] if has { purchaseId[v.Id] = struct{}{} } else { articleIds = append(articleIds, v.Id) } } articleIds = nil purchaseData, err := a.purchaseRepo.FindArticleById(user.Id, articleIds...) if err != nil { a.log.Error(err.Error()) } if purchaseData != nil { for _, v := range purchaseData { purchaseId[v.ContentId] = struct{}{} } } articleIds = nil } result := make([]*ArticleDto, 0, len(articles)) for _, v := range articles { t := time.Unix(v.Ctime, 0) // 秒数和纳秒数,0 表示没有纳秒部分 m, g := a.getMainGrowthBoard(v.Stocks) _, lock := purchaseId[v.Id] result = append(result, &ArticleDto{ EventId: v.Id, Title: v.Title, Class: class_type[v.Type], ReleaseDate: t.Format("2006-01-02"), ReleaseTime: t.Format("15:04"), Brief: v.Brief, MainBoard: m, GrowthBoard: g, Unlock: lock, }) } purchaseId = nil articles = nil page.Content = &result return nil } func (a *ArticleService) FindUnLock(ePhone string, page *page.Page, searchParams map[string]string) error { user, err := a.userRepo.FindByPhone(ePhone) if err != nil { a.log.Error(err) return err } conds := make([]builder.Cond, 0) class := searchParams["search_like_class"] if class != "" { classCode := class_type_reverse[class] if classCode != 0 { conds = append(conds, builder.Eq{"type": classCode}) } } purchaseData, err := a.purchaseRepo.FindArticlesByUserId(user.Id) if err != nil { a.log.Error(err) return err } if len(purchaseData) == 0 { return nil } pId := make([]uint64, 0, len(purchaseData)) for _, v := range purchaseData { pId = append(pId, v.ContentId) } conds = append(conds, builder.In("id")) articles := make([]*article.LianV1Article, 0) page.Content = &articles err = a.repo.Find(page, conds) if err != nil { a.log.Error(err) return err } result := make([]*ArticleDto, 0, len(articles)) for _, v := range articles { t := time.Unix(v.Ctime, 0) // 秒数和纳秒数,0 表示没有纳秒部分 m, g := a.getMainGrowthBoard(v.Stocks) result = append(result, &ArticleDto{ EventId: v.Id, Title: v.Title, Class: class_type[v.Type], ReleaseDate: t.Format("2006-01-02"), ReleaseTime: t.Format("15:04"), Brief: v.Brief, Content: v.Content, Stocks: v.Stocks, MainBoard: m, GrowthBoard: g, Unlock: false, }) } articles = nil page.Content = &result return nil } func (a *ArticleService) FindFree(page *page.Page, searchParams map[string]string) error { conds := make([]builder.Cond, 0) class := searchParams["search_like_class"] if class != "" { classCode := class_type_reverse[class] if classCode != 0 { conds = append(conds, builder.Eq{"type": classCode}) } } conds = append(conds, builder.And(builder.Lt{"ctime": time.Now().AddDate(0, 0, -7).Unix()})) page.PageSize = 20 articles := make([]*article.LianV1Article, 0) page.Content = &articles err := a.repo.Find(page, conds) if err != nil { a.log.Error(err) return err } result := make([]*ArticleDto, 0, len(articles)) for _, v := range articles { t := time.Unix(v.Ctime, 0) // 秒数和纳秒数,0 表示没有纳秒部分 m, g := a.getMainGrowthBoard(v.Stocks) result = append(result, &ArticleDto{ EventId: v.Id, Title: v.Title, Class: class_type[v.Type], ReleaseDate: t.Format("2006-01-02"), ReleaseTime: t.Format("15:04"), Brief: v.Brief, Stocks: v.Stocks, Content: v.Content, MainBoard: m, GrowthBoard: g, Unlock: true, }) } articles = nil page.Content = &result return nil } func (a *ArticleService) Detail(userPhone string, id uint64) (*ArticleDto, error) { user, err := a.userRepo.FindByPhone(userPhone) if err != nil { a.log.Error(err) return nil, err } p, err := a.purchaseRepo.FindByUserIdAndContent(user.Id, id, purchase.ContentTypeArticle) if err != nil { a.log.Error(err) return nil, err } articleDto := &ArticleDto{} if p == nil { articleDto.Unlock = false return articleDto, nil } article, err := a.repo.GetArticleById(id) if err != nil { a.log.Error(err) return nil, err } t := time.Unix(article.Ctime, 0) m, g := a.getMainGrowthBoard(article.Stocks) return &ArticleDto{ EventId: article.Id, Title: article.Title, Class: class_type[article.Type], ReleaseDate: t.Format("2006-01-02"), ReleaseTime: t.Format("15:04"), Stocks: article.Stocks, Brief: article.Brief, MainBoard: m, GrowthBoard: g, Content: article.Content, Unlock: true, }, nil } func (a *ArticleService) UnLockArticle(ePhone string, aid uint64) (*ArticleDto, error) { // 1. 获取用户聚合根 userAggregate, err := a.userAggregateRepo.GetUserAggregate(ePhone) if err != nil { return nil, fmt.Errorf("获取用户信息失败: %w", err) } // 2. 获取文章 article, err := a.repo.GetArticleById(aid) if err != nil { return nil, fmt.Errorf("获取文章信息失败: %w", err) } // 3. 执行领域逻辑 if err := userAggregate.UnlockArticle(aid); err != nil { return nil, fmt.Errorf("解锁文章失败: %w", err) } // 4. 保存聚合根状态 if err := a.userAggregateRepo.SaveUserAggregate(userAggregate); err != nil { return nil, fmt.Errorf("保存用户状态失败: %w", err) } // 5. 返回结果 return &ArticleDto{ Stocks: article.Stocks, Content: article.Content, Unlock: true, }, nil } func (a ArticleService) getMainGrowthBoard(stocks string) (int, int) { if stocks == "" { return 0, 0 } // 将字符串分割成股票代码数组 stockList := strings.Split(stocks, ",") mainBoardCount := 0 // 主板数量 growthBoardCount := 0 // 创业板数量 for _, stock := range stockList { // 去除可能的空格 stock = strings.TrimSpace(stock) // 检查股票代码长度是否合法(sz/sh + 6位数字) if len(stock) < 8 { continue } // 获取股票代码(去除市场前缀) prefix := strings.ToLower(stock[0:2]) // 转换为小写 code := stock[2:] // 检查前缀是否合法 if prefix != "sz" && prefix != "sh" { continue } switch { // 创业板: 300xxx 或 301xxx case strings.HasPrefix(code, "300") || strings.HasPrefix(code, "301"): growthBoardCount++ // 主板: // 深圳主板 00xxxx // 上海主板 60xxxx case strings.HasPrefix(code, "000") || strings.HasPrefix(code, "001") || strings.HasPrefix(code, "002") || strings.HasPrefix(code, "600") || strings.HasPrefix(code, "601") || strings.HasPrefix(code, "603") || strings.HasPrefix(code, "605"): mainBoardCount++ } } return mainBoardCount, growthBoardCount }