ai_scheduler/internal/biz/advice_activity.go

239 lines
6.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package biz
import (
"ai_scheduler/internal/data/mongo_model"
"ai_scheduler/internal/entitys"
"ai_scheduler/internal/pkg"
"context"
"errors"
"fmt"
"strings"
"time"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo/options"
)
// AdviceActivityBiz 产品活动管理(后台发布,用于 AI 主动对话与活动推送素材)
type AdviceActivityBiz struct {
mongo *pkg.Mongo
activityMongo *mongo_model.AdvicerActivityMongo
}
func NewAdviceActivityBiz(
mongo *pkg.Mongo,
activityMongo *mongo_model.AdvicerActivityMongo,
) *AdviceActivityBiz {
return &AdviceActivityBiz{
mongo: mongo,
activityMongo: activityMongo,
}
}
// Add 新增产品活动
func (a *AdviceActivityBiz) Add(ctx context.Context, param *entitys.AdvicerActivityAddReq) (id interface{}, err error) {
name := strings.TrimSpace(param.Name)
if len(name) == 0 {
return nil, errors.New("活动名称不能为空")
}
var startAt, endAt time.Time
if len(strings.TrimSpace(param.StartAt)) != 0 {
if startAt, err = parseTimeStr(param.StartAt); err != nil {
return nil, fmt.Errorf("开始时间格式错误: %w", err)
}
}
if len(strings.TrimSpace(param.EndAt)) != 0 {
if endAt, err = parseTimeStr(param.EndAt); err != nil {
return nil, fmt.Errorf("结束时间格式错误: %w", err)
}
}
now := time.Now()
res, err := a.mongo.Co(a.activityMongo).InsertOne(ctx, &mongo_model.AdvicerActivityMongo{
ProjectId: param.ProjectId,
AdvicerId: param.AdvicerId,
Name: name,
Content: param.Content,
StartAt: startAt,
EndAt: endAt,
TargetLevels: param.TargetLevels,
Status: param.Status,
CreateAt: now,
UpdateAt: now,
})
if err != nil {
return nil, err
}
return res.InsertedID, nil
}
// Update 更新产品活动(未传字段保持原值)
func (a *AdviceActivityBiz) Update(ctx context.Context, param *entitys.AdvicerActivityUpdateReq) (err error) {
if len(param.Id) == 0 {
return errors.New("ID不能为空")
}
objectID, err := primitive.ObjectIDFromHex(param.Id)
if err != nil {
return fmt.Errorf("ID转换失败: %w", err)
}
set := bson.M{"updateAt": time.Now()}
if name := strings.TrimSpace(param.Name); len(name) != 0 {
set["name"] = name
}
if param.Content != "" {
set["content"] = param.Content
}
if len(strings.TrimSpace(param.StartAt)) != 0 {
t, e := parseTimeStr(param.StartAt)
if e != nil {
return fmt.Errorf("开始时间格式错误: %w", e)
}
set["startAt"] = t
}
if len(strings.TrimSpace(param.EndAt)) != 0 {
t, e := parseTimeStr(param.EndAt)
if e != nil {
return fmt.Errorf("结束时间格式错误: %w", e)
}
set["endAt"] = t
}
if param.TargetLevels != nil {
set["targetLevels"] = param.TargetLevels
}
if param.ProjectId != 0 {
set["projectId"] = param.ProjectId
}
if param.AdvicerId != 0 {
set["advicerId"] = param.AdvicerId
}
// 状态显式覆盖(0草稿/1进行中/2停止)
set["status"] = param.Status
_, err = a.mongo.Co(a.activityMongo).UpdateOne(ctx, bson.M{"_id": objectID}, bson.M{"$set": set})
return err
}
// List 活动列表查询(按更新时间倒序)
func (a *AdviceActivityBiz) List(ctx context.Context, param *entitys.AdvicerActivityListReq) (list []mongo_model.AdvicerActivityItem, err error) {
filter := bson.M{}
if param.ProjectId != 0 {
filter["projectId"] = param.ProjectId
}
if param.AdvicerId != 0 {
filter["advicerId"] = param.AdvicerId
}
if param.Status != nil {
filter["status"] = *param.Status
}
if len(param.Id) != 0 {
objectID, e := primitive.ObjectIDFromHex(param.Id)
if e != nil {
return nil, fmt.Errorf("ID转换失败: %w", e)
}
filter["_id"] = objectID
}
opts := options.Find().SetSort(bson.D{{Key: "updateAt", Value: -1}})
if param.PageSize > 0 {
page := param.Page
if page < 1 {
page = 1
}
size := int64(param.PageSize)
opts.SetSkip(int64(page-1) * size).SetLimit(size)
} else {
opts.SetLimit(500)
}
cursor, err := a.mongo.Co(a.activityMongo).Find(ctx, filter, opts)
if err != nil {
return nil, err
}
for cursor.Next(ctx) {
var item mongo_model.AdvicerActivityItem
if err = cursor.Decode(&item); err != nil {
return nil, err
}
list = append(list, item)
}
if err = cursor.Err(); err != nil {
return nil, err
}
return list, nil
}
// Del 删除活动
func (a *AdviceActivityBiz) Del(ctx context.Context, param *entitys.AdvicerActivityDelReq) (err error) {
if len(param.Id) == 0 {
return errors.New("ID不能为空")
}
objectID, err := primitive.ObjectIDFromHex(param.Id)
if err != nil {
return fmt.Errorf("ID转换失败: %w", err)
}
_, err = a.mongo.Co(a.activityMongo).DeleteOne(ctx, bson.M{"_id": objectID})
return err
}
// Info 活动详情
func (a *AdviceActivityBiz) Info(ctx context.Context, param *entitys.AdvicerActivityInfoReq) (info mongo_model.AdvicerActivityMongo, err error) {
if len(param.Id) == 0 {
return info, errors.New("ID不能为空")
}
objectID, err := primitive.ObjectIDFromHex(param.Id)
if err != nil {
return info, fmt.Errorf("ID转换失败: %w", err)
}
res := a.mongo.Co(a.activityMongo).FindOne(ctx, bson.M{"_id": objectID})
if res.Err() != nil {
return info, res.Err()
}
if err = res.Decode(&info); err != nil {
return info, err
}
return info, nil
}
// ActiveList 查询当前生效的活动(状态启用 + 已在时间范围内),供 AI 主动对话/推送取素材。
// level 非空时只返回"未限定等级"或目标等级匹配的活动。
func (a *AdviceActivityBiz) ActiveList(ctx context.Context, projectId int32, level string) (list []mongo_model.AdvicerActivityItem, err error) {
now := time.Now()
filter := bson.M{
"status": mongo_model.ActivityStatusActive,
"startAt": bson.M{"$lte": now},
}
if projectId != 0 {
filter["projectId"] = projectId
}
// endAt 为零值视为长期有效,否则需在结束时间之前
filter["$or"] = []bson.M{
{"endAt": bson.M{"$gte": now}},
{"endAt": time.Time{}},
}
if len(level) != 0 {
filter["$and"] = []bson.M{{
"$or": []bson.M{
{"targetLevels": bson.M{"$size": 0}},
{"targetLevels": level},
},
}}
}
cursor, err := a.mongo.Co(a.activityMongo).Find(ctx, filter, options.Find().SetSort(bson.D{{Key: "startAt", Value: -1}}))
if err != nil {
return nil, err
}
for cursor.Next(ctx) {
var item mongo_model.AdvicerActivityItem
if err = cursor.Decode(&item); err != nil {
return nil, err
}
list = append(list, item)
}
if err = cursor.Err(); err != nil {
return nil, err
}
return list, nil
}