feat: 迁移plant

This commit is contained in:
Blizzard
2026-05-23 13:55:05 +08:00
parent a93477ea8e
commit ae6d03d351
228 changed files with 25296 additions and 917 deletions
@@ -2,11 +2,14 @@ package logic
import (
"context"
"time"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
"github.com/zeromicro/go-zero/core/logx"
"gorm.io/gorm"
)
type AddCarePlanLogic struct {
@@ -23,9 +26,38 @@ func NewAddCarePlanLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AddCa
}
}
// 养护
// 添加魏护计划
func (l *AddCarePlanLogic) AddCarePlan(in *plant.AddCarePlanReq) (*plant.CommonResp, error) {
// todo: add your logic here and delete this line
return &plant.CommonResp{}, nil
txErr := l.svcCtx.DB.Transaction(func(tx *gorm.DB) error {
// 1. 创建计划
carePlan := plantModel.CarePlan{
UserID: in.UserId,
PlantID: in.PlantId,
Name: in.Name,
Icon: in.Icon,
Period: int(in.Period),
TargetAction: in.TargetAction,
}
if err := tx.Create(&carePlan).Error; err != nil {
return err
}
// 2. 创建第一个魏护任务
today := time.Now().Truncate(24 * time.Hour)
dueDate := today.AddDate(0, 0, int(in.Period))
task := plantModel.CareTask{
UserID: in.UserId,
PlantID: in.PlantId,
PlanID: carePlan.ID,
Name: in.Name,
Icon: in.Icon,
TargetAction: in.TargetAction,
DueDate: dueDate,
Status: 1,
}
return tx.Create(&task).Error
})
if txErr != nil {
return nil, txErr
}
return &plant.CommonResp{Code: 0, Msg: "ok"}, nil
}
@@ -2,11 +2,14 @@ package logic
import (
"context"
"time"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
"github.com/zeromicro/go-zero/core/logx"
"gorm.io/gorm"
)
type AddCareRecordLogic struct {
@@ -23,8 +26,74 @@ func NewAddCareRecordLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Add
}
}
// AddCareRecord 添加养护记录:完成当前任务 + 生成下一期任务 + 更新统计
func (l *AddCareRecordLogic) AddCareRecord(in *plant.AddCareRecordReq) (*plant.CommonResp, error) {
// todo: add your logic here and delete this line
txErr := l.svcCtx.DB.Transaction(func(tx *gorm.DB) error {
// 1. 查找计划信息(获取 period 和图标用于生成下一任务)
var plan plantModel.CarePlan
if err := tx.Where("id = ?", in.PlanId).First(&plan).Error; err != nil {
return err
}
return &plant.CommonResp{}, nil
// 2. 保存养护记录
record := plantModel.CareRecord{
UserID: in.UserId,
PlantID: in.PlantId,
PlanID: in.PlanId,
Name: plan.Name,
Icon: plan.Icon,
Remark: in.Note,
}
if err := tx.Create(&record).Error; err != nil {
return err
}
// 3. 把该计划当前最旧的 pending 任务标记为已完成
tx.Model(&plantModel.CareTask{}).
Where("plan_id = ? AND status = 1", in.PlanId).
Order("due_date asc").
Limit(1).
Update("status", 2)
// 4. 生成下一期任务(以今天为基准,+period 天)
nextDue := time.Now().Truncate(24*time.Hour).AddDate(0, 0, plan.Period)
nextTask := plantModel.CareTask{
UserID: in.UserId,
PlantID: in.PlantId,
PlanID: in.PlanId,
Name: plan.Name,
Icon: plan.Icon,
TargetAction: plan.TargetAction,
DueDate: nextDue,
Status: 1,
}
if err := tx.Create(&nextTask).Error; err != nil {
return err
}
// 5. 更新用户 care_count 统计
actionMap := map[string]string{
"water": "water_count",
"fertilize": "fertilize_count",
"repot": "repot_count",
"prune": "prune_count",
"photo": "photo_count",
}
colName := "care_count"
if col, ok := actionMap[plan.TargetAction]; ok {
colName = col
}
tx.Model(&plantModel.UserProfile{}).
Where("user_id = ?", in.UserId).
Updates(map[string]interface{}{
"care_count": gorm.Expr("care_count + 1"),
colName: gorm.Expr(colName + " + 1"),
})
return nil
})
if txErr != nil {
return nil, txErr
}
return &plant.CommonResp{Code: 0, Msg: "ok"}, nil
}
@@ -2,11 +2,11 @@ package logic
import (
"context"
"github.com/zeromicro/go-zero/core/logx"
"gorm.io/gorm"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
"github.com/zeromicro/go-zero/core/logx"
)
type AddGrowthRecordLogic struct {
@@ -16,15 +16,33 @@ type AddGrowthRecordLogic struct {
}
func NewAddGrowthRecordLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AddGrowthRecordLogic {
return &AddGrowthRecordLogic{
ctx: ctx,
svcCtx: svcCtx,
Logger: logx.WithContext(ctx),
}
return &AddGrowthRecordLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
func (l *AddGrowthRecordLogic) AddGrowthRecord(in *plant.AddGrowthRecordReq) (*plant.CommonResp, error) {
// todo: add your logic here and delete this line
return &plant.CommonResp{}, nil
err := l.svcCtx.DB.Transaction(func(tx *gorm.DB) error {
record := plantModel.GrowthRecord{
UserID: in.UserId, PlantID: in.PlantId, Content: in.Content,
}
if err := tx.Create(&record).Error; err != nil {
return err
}
// 保存图片关联
if len(in.ImgIds) > 0 {
relations := make([]plantModel.GrowthRecordOss, 0, len(in.ImgIds))
for _, ossId := range in.ImgIds {
relations = append(relations, plantModel.GrowthRecordOss{
GrowthRecordID: record.ID, OssID: ossId,
})
}
if err := tx.Create(&relations).Error; err != nil {
return err
}
}
return nil
})
if err != nil {
return nil, err
}
return &plant.CommonResp{Code: 0, Msg: "ok"}, nil
}
@@ -0,0 +1,26 @@
package logic
import (
"context"
"github.com/zeromicro/go-zero/core/logx"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
)
type ClearAiChatHistoryLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewClearAiChatHistoryLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ClearAiChatHistoryLogic {
return &ClearAiChatHistoryLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
func (l *ClearAiChatHistoryLogic) ClearAiChatHistory(in *plant.GetProfileReq) (*plant.CommonResp, error) {
if err := l.svcCtx.DB.Where("user_id = ?", in.UserId).Delete(&plantModel.AiChatHistory{}).Error; err != nil {
return nil, err
}
return &plant.CommonResp{Code: 0, Msg: "ok"}, nil
}
@@ -3,10 +3,12 @@ package logic
import (
"context"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
"github.com/zeromicro/go-zero/core/logx"
"gorm.io/gorm"
)
type CommentPostLogic struct {
@@ -23,8 +25,23 @@ func NewCommentPostLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Comme
}
}
// 评论帖子
func (l *CommentPostLogic) CommentPost(in *plant.CommentPostReq) (*plant.CommonResp, error) {
// todo: add your logic here and delete this line
return &plant.CommonResp{}, nil
err := l.svcCtx.DB.Transaction(func(tx *gorm.DB) error {
comment := plantModel.PostComment{
PostID: in.PostId,
UserID: in.UserId,
Content: in.Content,
ParentID: in.ParentId,
}
if err := tx.Create(&comment).Error; err != nil {
return err
}
return tx.Model(&plantModel.Post{}).Where("id = ?", in.PostId).
Update("comment_count", gorm.Expr("comment_count + 1")).Error
})
if err != nil {
return nil, err
}
return &plant.CommonResp{Code: 0, Msg: "ok"}, nil
}
@@ -0,0 +1,164 @@
package logic
import (
"context"
"errors"
"time"
"github.com/zeromicro/go-zero/core/logx"
"gorm.io/gorm"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
)
type CompleteTaskLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewCompleteTaskLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CompleteTaskLogic {
return &CompleteTaskLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
const taskReward = int64(50)
func (l *CompleteTaskLogic) CompleteTask(in *plant.CompleteTaskReq) (*plant.TaskCompletionResult, error) {
var result plant.TaskCompletionResult
err := l.svcCtx.DB.Transaction(func(tx *gorm.DB) error {
// 1. 查任务
var task plantModel.CareTask
if err := tx.Set("gorm:query_option", "FOR UPDATE").
Where("id = ? AND user_id = ?", in.TaskId, in.UserId).First(&task).Error; err != nil {
return err
}
if task.Status != 1 {
return errors.New("任务已完成或已过期,不能重复完成")
}
// 2. 标记完成
now := time.Now()
if err := tx.Model(&task).Updates(map[string]interface{}{
"status": 2, "completed_at": now,
}).Error; err != nil {
return err
}
// 3. 查计划,生成下个周期任务
var plan plantModel.CarePlan
if err := tx.Where("id = ?", task.PlanID).First(&plan).Error; err != nil {
return err
}
today := time.Now()
todayZero := time.Date(today.Year(), today.Month(), today.Day(), 0, 0, 0, 0, today.Location())
nextDue := todayZero.AddDate(0, 0, plan.Period)
newTask := plantModel.CareTask{
UserID: plan.UserID, PlantID: plan.PlantID, PlanID: plan.ID,
Name: plan.Name, Icon: plan.Icon, TargetAction: plan.TargetAction,
DueDate: nextDue, Status: 1,
}
if err := tx.Create(&newTask).Error; err != nil {
return err
}
// 4. 保存养护记录
record := plantModel.CareRecord{
UserID: plan.UserID, PlantID: plan.PlantID, PlanID: plan.ID,
Name: plan.Name, Icon: plan.Icon, Remark: in.Remark,
}
if err := tx.Create(&record).Error; err != nil {
return err
}
// 5. 查用户 profile(加锁)
var profile plantModel.UserProfile
if err := tx.Set("gorm:query_option", "FOR UPDATE").
Where("user_id = ?", in.UserId).First(&profile).Error; err != nil {
return err
}
fieldMap := map[string]string{
"ACT_WATER": "water_count", "ACT_FERTILIZE": "fertilize_count",
"ACT_REPOT": "repot_count", "ACT_PRUNE": "prune_count",
}
col, ok := fieldMap[task.TargetAction]
if !ok {
col = "care_count"
}
newTotal := profile.TotalSunlight + taskReward
// 5.1 等级判定
var latestLevel plantModel.LevelConfig
levelErr := tx.Where("min_sunlight <= ?", newTotal).
Order("min_sunlight DESC").First(&latestLevel).Error
if levelErr != nil {
if errors.Is(levelErr, gorm.ErrRecordNotFound) {
// 没有等级配置,跳过等级相关逻辑
result.IsLevelUp = false
result.RewardSunlight = taskReward
// 仍要更新阳光值
profileData := map[string]interface{}{
col: gorm.Expr(col + " + 1"),
"current_sunlight": profile.CurrentSunlight + taskReward,
"total_sunlight": newTotal,
}
if err := tx.Model(&plantModel.UserProfile{}).
Where("user_id = ?", in.UserId).Updates(profileData).Error; err != nil {
return err
}
return nil // 跳过后续等级/徽章逻辑
}
return errors.New("等级配置异常")
}
result.IsLevelUp = (latestLevel.ID != profile.LevelID)
result.CurrentLevel = &plant.LevelConfigInfo{
Id: latestLevel.ID, Level: int32(latestLevel.Level),
Title: latestLevel.Title, MinSunlight: latestLevel.MinSunlight,
}
result.RewardSunlight = taskReward
// 5.2 更新 profile
profileData := map[string]interface{}{
col: gorm.Expr(col + " + 1"),
"current_sunlight": profile.CurrentSunlight + taskReward,
"total_sunlight": newTotal,
"level_id": latestLevel.ID,
}
if err := tx.Model(&plantModel.UserProfile{}).
Where("user_id = ?", in.UserId).Updates(profileData).Error; err != nil {
return err
}
// 6. 徽章判定
actionValMap := map[string]int64{
"ACT_WATER": profile.WaterCount + 1, "ACT_FERTILIZE": profile.FertilizeCount + 1,
"ACT_REPOT": profile.RepotCount + 1, "ACT_PRUNE": profile.PruneCount + 1,
}
currentVal := actionValMap[task.TargetAction]
var ownedIds []string
tx.Model(&plantModel.UserBadge{}).Where("user_id = ?", in.UserId).Pluck("badge_id", &ownedIds)
ownedMap := make(map[string]bool)
for _, id := range ownedIds {
ownedMap[id] = true
}
var badgeConfigs []plantModel.BadgeConfig
tx.Where("target_action = ?", task.TargetAction).Find(&badgeConfigs)
var bestBadge *plantModel.BadgeConfig
for i := range badgeConfigs {
bc := &badgeConfigs[i]
if !ownedMap[bc.ID] && currentVal >= bc.Threshold {
if bestBadge == nil || bc.Tier > bestBadge.Tier {
bestBadge = bc
}
}
}
if bestBadge != nil {
ub := plantModel.UserBadge{UserID: in.UserId, BadgeID: bestBadge.ID}
if err := tx.Create(&ub).Error; err != nil {
return err
}
tx.Model(&plantModel.UserProfile{}).Where("user_id = ?", in.UserId).
UpdateColumn("current_sunlight", gorm.Expr("current_sunlight + ?", bestBadge.RewardSunlight))
result.IsGetBadge = true
result.NewBadge = &plant.BadgeConfigInfo{
Id: bestBadge.ID, Name: bestBadge.Name, Dimension: bestBadge.Dimension,
Tier: int32(bestBadge.Tier), RewardSunlight: bestBadge.RewardSunlight,
}
}
return nil
})
return &result, err
}
@@ -0,0 +1,32 @@
package logic
import (
"context"
"github.com/zeromicro/go-zero/core/logx"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
)
type CreateBadgeConfigLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewCreateBadgeConfigLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateBadgeConfigLogic {
return &CreateBadgeConfigLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
func (l *CreateBadgeConfigLogic) CreateBadgeConfig(in *plant.CreateBadgeConfigReq) (*plant.CommonResp, error) {
cfg := plantModel.BadgeConfig{
Name: in.Name, Description: in.Description, Dimension: in.Dimension,
GroupID: in.GroupId, Tier: int(in.Tier), TargetAction: in.TargetAction,
Threshold: in.Threshold, RewardSunlight: in.RewardSunlight,
IconID: in.IconId, Sort: int(in.Sort),
}
if err := l.svcCtx.DB.Create(&cfg).Error; err != nil {
return nil, err
}
return &plant.CommonResp{Code: 0, Msg: "ok"}, nil
}
@@ -0,0 +1,56 @@
package logic
import (
"context"
"time"
"github.com/zeromicro/go-zero/core/logx"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
)
type CreateExchangeItemLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewCreateExchangeItemLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateExchangeItemLogic {
return &CreateExchangeItemLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
func (l *CreateExchangeItemLogic) CreateExchangeItem(in *plant.CreateExchangeItemReq) (*plant.CommonResp, error) {
cost := in.Cost
if in.CostSunlight > 0 {
cost = in.CostSunlight
}
itemType := in.Type
if itemType == "" {
itemType = "PHYSICAL"
}
imgID := in.ImgId
if imgID == "" {
imgID = in.ImageId
}
var startTime, endTime *time.Time
if in.StartTime != "" {
if t, err := time.Parse(time.DateTime, in.StartTime); err == nil {
startTime = &t
}
}
if in.EndTime != "" {
if t, err := time.Parse(time.DateTime, in.EndTime); err == nil {
endTime = &t
}
}
item := plantModel.ExchangeItem{
Name: in.Name, Desc: in.Desc, Description: in.Desc, ImgID: imgID, ImageID: imgID,
Type: itemType, Cost: cost, CostSunlight: cost, Stock: int(in.Stock), Status: 1,
LimitPerUser: int(in.LimitPerUser), Sort: int(in.Sort), StartTime: startTime, EndTime: endTime,
}
if err := l.svcCtx.DB.Create(&item).Error; err != nil {
return nil, err
}
return &plant.CommonResp{Code: 0, Msg: "ok"}, nil
}
@@ -2,11 +2,15 @@ package logic
import (
"context"
"errors"
"time"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
"github.com/zeromicro/go-zero/core/logx"
"gorm.io/gorm"
)
type CreateExchangeOrderLogic struct {
@@ -23,8 +27,92 @@ func NewCreateExchangeOrderLogic(ctx context.Context, svcCtx *svc.ServiceContext
}
}
// 创建兑换订单(阳光值扣减+库存扣减事务)
func (l *CreateExchangeOrderLogic) CreateExchangeOrder(in *plant.CreateExchangeOrderReq) (*plant.CommonResp, error) {
// todo: add your logic here and delete this line
txErr := l.svcCtx.DB.Transaction(func(tx *gorm.DB) error {
// 1. 查询并锁定商品
var item plantModel.ExchangeItem
if err := tx.Set("gorm:query_option", "FOR UPDATE").
Where("id = ? AND status = 1", in.ItemId).First(&item).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return errors.New("商品不存在或已下架")
}
return err
}
return &plant.CommonResp{}, nil
quantity := int(in.Quantity)
if quantity <= 0 {
quantity = 1
}
cost := item.CostSunlight
if cost == 0 {
cost = item.Cost
}
totalCost := cost * int64(quantity)
now := time.Now()
if item.StartTime != nil && now.Before(*item.StartTime) {
return errors.New("兑换尚未开始")
}
if item.EndTime != nil && now.After(*item.EndTime) {
return errors.New("兑换已结束")
}
if item.LimitPerUser > 0 {
var count int64
tx.Model(&plantModel.ExchangeOrder{}).
Where("user_id = ? AND item_id = ? AND status <> ?", in.UserId, in.ItemId, 5).
Count(&count)
if int(count)+quantity > item.LimitPerUser {
return errors.New("已达到兑换上限")
}
}
// 2. 检查库存(-1 表示无限库存)
if item.Stock >= 0 && item.Stock < quantity {
return errors.New("库存不足")
}
// 3. 扣减阳光值(原子操作,RowsAffected==0 说明阳光不足)
result := tx.Model(&plantModel.UserProfile{}).
Where("user_id = ? AND current_sunlight >= ?", in.UserId, totalCost).
Update("current_sunlight", gorm.Expr("current_sunlight - ?", totalCost))
if result.Error != nil {
return result.Error
}
if result.RowsAffected == 0 {
return errors.New("阳光值不足")
}
// 4. 扣减库存(-1无限库存时跳过)
if item.Stock >= 0 {
if err := tx.Model(&plantModel.ExchangeItem{}).
Where("id = ? AND stock >= ?", in.ItemId, quantity).
Update("stock", gorm.Expr("stock - ?", quantity)).Error; err != nil {
return err
}
}
// 5. 创建订单
order := plantModel.ExchangeOrder{
UserID: in.UserId,
ItemID: in.ItemId,
ItemName: item.Name,
Cost: totalCost,
CostSunlight: totalCost,
Quantity: quantity,
ItemType: item.Type,
RecipientName: in.RecipientName,
Phone: in.Phone,
Address: in.Address,
Status: 1,
}
if item.Type == "VIRTUAL" {
order.Status = 4
order.CompletedAt = &now
}
return tx.Create(&order).Error
})
if txErr != nil {
return nil, txErr
}
return &plant.CommonResp{Code: 0, Msg: "ok"}, nil
}
@@ -0,0 +1,27 @@
package logic
import (
"context"
"github.com/zeromicro/go-zero/core/logx"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
)
type CreateLevelConfigLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewCreateLevelConfigLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateLevelConfigLogic {
return &CreateLevelConfigLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
func (l *CreateLevelConfigLogic) CreateLevelConfig(in *plant.CreateLevelConfigReq) (*plant.CommonResp, error) {
cfg := plantModel.LevelConfig{Level: int(in.Level), Title: in.Title, MinSunlight: in.MinSunlight, Perks: in.Perks}
if err := l.svcCtx.DB.Create(&cfg).Error; err != nil {
return nil, err
}
return &plant.CommonResp{Code: 0, Msg: "ok"}, nil
}
@@ -2,11 +2,14 @@ package logic
import (
"context"
"time"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
"github.com/zeromicro/go-zero/core/logx"
"gorm.io/gorm"
)
type CreatePlantLogic struct {
@@ -23,9 +26,50 @@ func NewCreatePlantLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Creat
}
}
// 我的植物
// 创建植物
func (l *CreatePlantLogic) CreatePlant(in *plant.CreatePlantReq) (*plant.CommonResp, error) {
// todo: add your logic here and delete this line
// 解析植时间
plantTime, err := time.Parse("2006-01-02", in.PlantTime)
if err != nil {
plantTime = time.Now()
}
return &plant.CommonResp{}, nil
var myPlantID string
txErr := l.svcCtx.DB.Transaction(func(tx *gorm.DB) error {
myPlant := plantModel.MyPlant{
UserID: in.UserId,
Name: in.Name,
PlantTime: plantTime,
Status: 1,
Placement: in.Placement,
PotMaterial: in.PotMaterial,
PotSize: in.PotSize,
Sunlight: in.Sunlight,
PlantingMaterial: in.PlantingMaterial,
}
if err := tx.Create(&myPlant).Error; err != nil {
return err
}
myPlantID = myPlant.ID
// 处理图片关联
if len(in.ImgIds) > 0 {
var relations []map[string]interface{}
for _, id := range in.ImgIds {
relations = append(relations, map[string]interface{}{
"sundynix_my_plant_id": myPlant.ID,
"sundynix_oss_id": id,
})
}
if err := tx.Table("sundynix_plant_my_plant_oss").Create(relations).Error; err != nil {
return err
}
}
// 更新用户 plant_count
return tx.Model(&plantModel.UserProfile{}).Where("user_id = ?", in.UserId).
Update("plant_count", gorm.Expr("plant_count + 1")).Error
})
if txErr != nil {
return nil, txErr
}
return &plant.CommonResp{Code: 0, Msg: myPlantID}, nil
}
@@ -3,10 +3,12 @@ package logic
import (
"context"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
"github.com/zeromicro/go-zero/core/logx"
"gorm.io/gorm"
)
type CreatePostLogic struct {
@@ -23,9 +25,34 @@ func NewCreatePostLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Create
}
}
// 社区
// 发布帖子
func (l *CreatePostLogic) CreatePost(in *plant.CreatePostReq) (*plant.CommonResp, error) {
// todo: add your logic here and delete this line
return &plant.CommonResp{}, nil
err := l.svcCtx.DB.Transaction(func(tx *gorm.DB) error {
post := plantModel.Post{
UserID: in.UserId,
Title: in.Title,
Content: in.Content,
Location: in.Location,
}
if err := tx.Create(&post).Error; err != nil {
return err
}
if len(in.ImgIds) > 0 {
var relations []map[string]interface{}
for _, id := range in.ImgIds {
relations = append(relations, map[string]interface{}{
"post_id": post.ID,
"oss_id": id,
})
}
if err := tx.Table("sundynix_post_oss").Create(relations).Error; err != nil {
return err
}
}
return nil
})
if err != nil {
return nil, err
}
return &plant.CommonResp{Code: 0, Msg: "ok"}, nil
}
@@ -2,11 +2,15 @@ package logic
import (
"context"
"errors"
"time"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
"github.com/zeromicro/go-zero/core/logx"
"gorm.io/gorm"
)
type CreateTopicLogic struct {
@@ -23,8 +27,29 @@ func NewCreateTopicLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Creat
}
}
// 创建话题
func (l *CreateTopicLogic) CreateTopic(in *plant.CreateTopicReq) (*plant.CommonResp, error) {
// todo: add your logic here and delete this line
return &plant.CommonResp{}, nil
name := in.Name
if name == "" {
name = in.Title
}
if !errors.Is(l.svcCtx.DB.Where("name = ? OR title = ?", name, in.Title).First(&plantModel.Topic{}).Error, gorm.ErrRecordNotFound) {
return nil, errors.New("存在重复话题")
}
var start, end *time.Time
if in.StartTime != "" {
if t, err := time.Parse(time.DateTime, in.StartTime); err == nil {
start = &t
}
}
if in.EndTime != "" {
if t, err := time.Parse(time.DateTime, in.EndTime); err == nil {
end = &t
}
}
topic := plantModel.Topic{Name: name, Title: in.Title, Icon: in.Icon, Desc: in.Desc, Remark: in.Remark, StartTime: start, EndTime: end}
if err := l.svcCtx.DB.Create(&topic).Error; err != nil {
return nil, err
}
return &plant.CommonResp{Code: 0, Msg: "ok"}, nil
}
@@ -3,6 +3,7 @@ package logic
import (
"context"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
@@ -23,8 +24,14 @@ func NewCreateWikiClassLogic(ctx context.Context, svcCtx *svc.ServiceContext) *C
}
}
// 创建百科分类
func (l *CreateWikiClassLogic) CreateWikiClass(in *plant.CreateWikiClassReq) (*plant.CommonResp, error) {
// todo: add your logic here and delete this line
return &plant.CommonResp{}, nil
wikiClass := plantModel.WikiClass{
Name: in.Name,
OssID: in.Icon,
}
if err := l.svcCtx.DB.Create(&wikiClass).Error; err != nil {
return nil, err
}
return &plant.CommonResp{Code: 0, Msg: "ok"}, nil
}
@@ -0,0 +1,56 @@
package logic
import (
"context"
"errors"
"github.com/zeromicro/go-zero/core/logx"
"gorm.io/gorm"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
)
type CreateWikiLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewCreateWikiLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateWikiLogic {
return &CreateWikiLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
func (l *CreateWikiLogic) CreateWiki(in *plant.CreateWikiReq) (*plant.CommonResp, error) {
if !errors.Is(l.svcCtx.DB.Where("name LIKE ?", "%"+in.Name+"%").First(&plantModel.Wiki{}).Error, gorm.ErrRecordNotFound) {
return nil, errors.New("植物已经存在")
}
classID := in.ClassId
if classID == "" && len(in.ClassIds) > 0 {
classID = in.ClassIds[0]
}
w := plantModel.Wiki{
Name: in.Name, LatinName: in.LatinName, Aliases: in.Aliases,
Genus: in.Genus, Difficulty: int(in.Difficulty), IsHot: int(in.IsHot),
GrowthHabit: in.GrowthHabit, LightIntensity: in.LightIntensity,
OptimalTempPeriod: in.OptimalTempPeriod, ClassID: classID,
DistributionArea: in.DistributionArea, LifeCycle: in.LifeCycle,
ReproductionMethod: in.ReproductionMethod, PestsDiseases: in.PestsDiseases,
LightType: in.LightType, Stem: in.Stem, Fruit: in.Fruit,
FoliageType: in.FoliageType, FoliageColor: in.FoliageColor,
FoliageShape: in.FoliageShape, Height: int(in.Height),
FloweringPeriod: in.FloweringPeriod, FloweringColor: in.FloweringColor,
FloweringShape: in.FloweringShape, FlowerDiameter: int(in.FloweringDiameter),
}
if err := l.svcCtx.DB.Transaction(func(tx *gorm.DB) error {
if err := tx.Create(&w).Error; err != nil {
return err
}
return replaceWikiRelations(tx, w.ID, in.ClassIds, in.OssIds, in.RelatedWikiIds)
}); err != nil {
return nil, err
}
go func() {
_, _ = NewSyncWikiVectorLogic(context.Background(), l.svcCtx).SyncWikiVector(&plant.SyncWikiVectorReq{WikiId: w.ID})
}()
return &plant.CommonResp{Code: 0, Msg: w.ID}, nil
}
@@ -0,0 +1,26 @@
package logic
import (
"context"
"github.com/zeromicro/go-zero/core/logx"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
)
type DeleteAiChatHistoryLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewDeleteAiChatHistoryLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeleteAiChatHistoryLogic {
return &DeleteAiChatHistoryLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
func (l *DeleteAiChatHistoryLogic) DeleteAiChatHistory(in *plant.IdsReq) (*plant.CommonResp, error) {
if err := l.svcCtx.DB.Where("id IN ?", in.Ids).Delete(&plantModel.AiChatHistory{}).Error; err != nil {
return nil, err
}
return &plant.CommonResp{Code: 0, Msg: "ok"}, nil
}
@@ -0,0 +1,26 @@
package logic
import (
"context"
"github.com/zeromicro/go-zero/core/logx"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
)
type DeleteBadgeConfigLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewDeleteBadgeConfigLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeleteBadgeConfigLogic {
return &DeleteBadgeConfigLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
func (l *DeleteBadgeConfigLogic) DeleteBadgeConfig(in *plant.IdsReq) (*plant.CommonResp, error) {
if err := l.svcCtx.DB.Where("id IN ?", in.Ids).Delete(&plantModel.BadgeConfig{}).Error; err != nil {
return nil, err
}
return &plant.CommonResp{Code: 0, Msg: "ok"}, nil
}
@@ -0,0 +1,28 @@
package logic
import (
"context"
"github.com/zeromicro/go-zero/core/logx"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
)
type DeleteCarePlanLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewDeleteCarePlanLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeleteCarePlanLogic {
return &DeleteCarePlanLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
func (l *DeleteCarePlanLogic) DeleteCarePlan(in *plant.IdsReq) (*plant.CommonResp, error) {
if err := l.svcCtx.DB.Where("id IN ?", in.Ids).Delete(&plantModel.CarePlan{}).Error; err != nil {
return nil, err
}
// 同步删除关联任务
l.svcCtx.DB.Where("plan_id IN ?", in.Ids).Delete(&plantModel.CareTask{})
return &plant.CommonResp{Code: 0, Msg: "ok"}, nil
}
@@ -0,0 +1,26 @@
package logic
import (
"context"
"github.com/zeromicro/go-zero/core/logx"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
)
type DeleteClassifyLogLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewDeleteClassifyLogLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeleteClassifyLogLogic {
return &DeleteClassifyLogLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
func (l *DeleteClassifyLogLogic) DeleteClassifyLog(in *plant.IdsReq) (*plant.CommonResp, error) {
if err := l.svcCtx.DB.Where("id IN ?", in.Ids).Delete(&plantModel.OcrLog{}).Error; err != nil {
return nil, err
}
return &plant.CommonResp{Code: 0, Msg: "ok"}, nil
}
@@ -0,0 +1,26 @@
package logic
import (
"context"
"github.com/zeromicro/go-zero/core/logx"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
)
type DeleteExchangeItemLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewDeleteExchangeItemLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeleteExchangeItemLogic {
return &DeleteExchangeItemLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
func (l *DeleteExchangeItemLogic) DeleteExchangeItem(in *plant.IdsReq) (*plant.CommonResp, error) {
if err := l.svcCtx.DB.Where("id IN ?", in.Ids).Delete(&plantModel.ExchangeItem{}).Error; err != nil {
return nil, err
}
return &plant.CommonResp{Code: 0, Msg: "ok"}, nil
}
@@ -0,0 +1,26 @@
package logic
import (
"context"
"github.com/zeromicro/go-zero/core/logx"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
)
type DeleteLevelConfigLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewDeleteLevelConfigLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeleteLevelConfigLogic {
return &DeleteLevelConfigLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
func (l *DeleteLevelConfigLogic) DeleteLevelConfig(in *plant.IdsReq) (*plant.CommonResp, error) {
if err := l.svcCtx.DB.Where("id IN ?", in.Ids).Delete(&plantModel.LevelConfig{}).Error; err != nil {
return nil, err
}
return &plant.CommonResp{Code: 0, Msg: "ok"}, nil
}
@@ -3,10 +3,12 @@ package logic
import (
"context"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
"github.com/zeromicro/go-zero/core/logx"
"gorm.io/gorm"
)
type DeletePlantLogic struct {
@@ -23,8 +25,32 @@ func NewDeletePlantLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Delet
}
}
// 删除植物 (级联删除魏护计划/任务/记录/图片)
func (l *DeletePlantLogic) DeletePlant(in *plant.IdsReq) (*plant.CommonResp, error) {
// todo: add your logic here and delete this line
return &plant.CommonResp{}, nil
txErr := l.svcCtx.DB.Transaction(func(tx *gorm.DB) error {
// 1. 清理图片中间表
tx.Exec("DELETE FROM sundynix_my_plant_oss WHERE sundynix_my_plant_id IN ?", in.Ids)
// 2. 删除魏护计划
if err := tx.Unscoped().Where("plant_id IN ?", in.Ids).Delete(&plantModel.CarePlan{}).Error; err != nil {
return err
}
// 3. 删除魏护任务
if err := tx.Unscoped().Where("plant_id IN ?", in.Ids).Delete(&plantModel.CareTask{}).Error; err != nil {
return err
}
// 4. 删除魏护记录
if err := tx.Unscoped().Where("plant_id IN ?", in.Ids).Delete(&plantModel.CareRecord{}).Error; err != nil {
return err
}
// 5. 删除成长记录
if err := tx.Unscoped().Where("plant_id IN ?", in.Ids).Delete(&plantModel.GrowthRecord{}).Error; err != nil {
return err
}
// 6. 删除植物本身
return tx.Unscoped().Where("id IN ?", in.Ids).Delete(&plantModel.MyPlant{}).Error
})
if txErr != nil {
return nil, txErr
}
return &plant.CommonResp{Code: 0, Msg: "ok"}, nil
}
@@ -3,10 +3,12 @@ package logic
import (
"context"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
"github.com/zeromicro/go-zero/core/logx"
"gorm.io/gorm"
)
type DeletePostLogic struct {
@@ -23,8 +25,23 @@ func NewDeletePostLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Delete
}
}
// 删除帖子 (级联删除评论/点赞/图片关联)
func (l *DeletePostLogic) DeletePost(in *plant.IdsReq) (*plant.CommonResp, error) {
// todo: add your logic here and delete this line
return &plant.CommonResp{}, nil
err := l.svcCtx.DB.Transaction(func(tx *gorm.DB) error {
tx.Exec("DELETE FROM sundynix_post_oss WHERE post_id IN ?", in.Ids)
if err := tx.Unscoped().Where("post_id IN ?", in.Ids).Delete(&plantModel.PostLike{}).Error; err != nil {
return err
}
if err := tx.Unscoped().Where("post_id IN ?", in.Ids).Delete(&plantModel.PostComment{}).Error; err != nil {
return err
}
if err := tx.Unscoped().Where("id IN ?", in.Ids).Delete(&plantModel.Post{}).Error; err != nil {
return err
}
return nil
})
if err != nil {
return nil, err
}
return &plant.CommonResp{Code: 0, Msg: "ok"}, nil
}
@@ -3,6 +3,7 @@ package logic
import (
"context"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
@@ -23,8 +24,10 @@ func NewDeleteTopicLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Delet
}
}
// 删除话题
func (l *DeleteTopicLogic) DeleteTopic(in *plant.IdsReq) (*plant.CommonResp, error) {
// todo: add your logic here and delete this line
return &plant.CommonResp{}, nil
if err := l.svcCtx.DB.Where("id in ?", in.Ids).Delete(&plantModel.Topic{}).Error; err != nil {
return nil, err
}
return &plant.CommonResp{Code: 0, Msg: "ok"}, nil
}
@@ -0,0 +1,26 @@
package logic
import (
"context"
"github.com/zeromicro/go-zero/core/logx"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
)
type DeleteWikiClassLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewDeleteWikiClassLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeleteWikiClassLogic {
return &DeleteWikiClassLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
func (l *DeleteWikiClassLogic) DeleteWikiClass(in *plant.IdsReq) (*plant.CommonResp, error) {
if err := l.svcCtx.DB.Where("id IN ?", in.Ids).Delete(&plantModel.WikiClass{}).Error; err != nil {
return nil, err
}
return &plant.CommonResp{Code: 0, Msg: "ok"}, nil
}
@@ -0,0 +1,38 @@
package logic
import (
"context"
"github.com/zeromicro/go-zero/core/logx"
"gorm.io/gorm"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
)
type DeleteWikiLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewDeleteWikiLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeleteWikiLogic {
return &DeleteWikiLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
func (l *DeleteWikiLogic) DeleteWiki(in *plant.IdsReq) (*plant.CommonResp, error) {
if err := l.svcCtx.DB.Transaction(func(tx *gorm.DB) error {
if err := tx.Where("id IN ?", in.Ids).Delete(&plantModel.Wiki{}).Error; err != nil {
return err
}
if err := tx.Where("wiki_id IN ?", in.Ids).Delete(&plantModel.WikiClassRelation{}).Error; err != nil {
return err
}
if err := tx.Where("wiki_id IN ?", in.Ids).Delete(&plantModel.WikiOss{}).Error; err != nil {
return err
}
return tx.Where("wiki_id IN ? OR related_wiki_id IN ?", in.Ids, in.Ids).Delete(&plantModel.WikiRelated{}).Error
}); err != nil {
return nil, err
}
return &plant.CommonResp{Code: 0, Msg: "ok"}, nil
}
@@ -0,0 +1,35 @@
package logic
import (
"context"
"errors"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
"github.com/zeromicro/go-zero/core/logx"
)
type DeleteWikiVectorLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewDeleteWikiVectorLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeleteWikiVectorLogic {
return &DeleteWikiVectorLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
func (l *DeleteWikiVectorLogic) DeleteWikiVector(in *plant.SyncWikiVectorReq) (*plant.CommonResp, error) {
if l.svcCtx.Config.Ai.QdrantUrl == "" || l.svcCtx.Config.Ai.QdrantCollection == "" {
return nil, errors.New("AI/RAG 未配置 QdrantUrl 或 QdrantCollection")
}
if err := deleteWikiVector(l.ctx, l.svcCtx.Config, in.WikiId); err != nil {
return nil, err
}
if err := l.svcCtx.DB.Model(&plantModel.Wiki{}).Where("id = ?", in.WikiId).Update("is_vector_synced", false).Error; err != nil {
return nil, err
}
return &plant.CommonResp{Code: 0, Msg: "ok"}, nil
}
@@ -0,0 +1,35 @@
package logic
import (
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/plant"
)
func exchangeItemInfo(item plantModel.ExchangeItem) *plant.ExchangeItemInfo {
cost := item.CostSunlight
if cost == 0 {
cost = item.Cost
}
imgID := item.ImageID
if imgID == "" {
imgID = item.ImgID
}
return &plant.ExchangeItemInfo{
Id: item.ID, Name: item.Name, Desc: item.Desc,
ImgId: imgID, Cost: cost, Stock: int32(item.Stock), Status: int32(item.Status),
}
}
func exchangeOrderInfo(o plantModel.ExchangeOrder) *plant.ExchangeOrderInfo {
completedAt := ""
if o.CompletedAt != nil {
completedAt = o.CompletedAt.Format("2006-01-02 15:04:05")
}
return &plant.ExchangeOrderInfo{
Id: o.ID, UserId: o.UserID, ItemId: o.ItemID, ItemName: o.ItemName,
Cost: o.Cost, Status: int32(o.Status), Address: o.Address,
CreatedAt: o.CreatedAt.Format("2006-01-02 15:04:05"),
Quantity: int32(o.Quantity), ItemType: o.ItemType, RecipientName: o.RecipientName,
Phone: o.Phone, TrackingNo: o.TrackingNo, Remark: o.Remark, CompletedAt: completedAt,
}
}
@@ -0,0 +1,42 @@
package logic
import (
"context"
"github.com/zeromicro/go-zero/core/logx"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
)
type GetAiChatHistoryLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewGetAiChatHistoryLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetAiChatHistoryLogic {
return &GetAiChatHistoryLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
func (l *GetAiChatHistoryLogic) GetAiChatHistory(in *plant.AiChatHistoryReq) (*plant.AiChatHistoryResp, error) {
var histories []plantModel.AiChatHistory
var total int64
db := l.svcCtx.DB.Model(&plantModel.AiChatHistory{}).Where("user_id = ?", in.UserId)
db.Count(&total)
page, size := in.Current, in.PageSize
if page < 1 {
page = 1
}
if size < 1 || size > 50 {
size = 20
}
db.Order("created_at desc").Offset(int((page - 1) * size)).Limit(int(size)).Find(&histories)
list := make([]*plant.AiChatHistoryInfo, 0, len(histories))
for _, h := range histories {
list = append(list, &plant.AiChatHistoryInfo{
Id: h.ID, UserId: h.UserID, Question: h.Question, Answer: h.Answer,
CreatedAt: h.CreatedAt.Format("2006-01-02 15:04:05"),
})
}
return &plant.AiChatHistoryResp{List: list, Total: total}, nil
}
@@ -0,0 +1,38 @@
package logic
import (
"context"
"github.com/zeromicro/go-zero/core/logx"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
"time"
)
type GetAiChatQuotaLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewGetAiChatQuotaLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetAiChatQuotaLogic {
return &GetAiChatQuotaLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
func (l *GetAiChatQuotaLogic) GetAiChatQuota(in *plant.GetProfileReq) (*plant.AiQuotaResp, error) {
now := time.Now()
todayStart := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
var used int64
l.svcCtx.DB.Model(&plantModel.AiChatHistory{}).
Where("user_id = ? AND created_at >= ?", in.UserId, todayStart).
Count(&used)
limit := l.svcCtx.Config.Ai.DailyQuota
if limit <= 0 {
limit = 20
}
remaining := limit - used
if remaining < 0 {
remaining = 0
}
return &plant.AiQuotaResp{Remaining: remaining, Total: limit, Used: used, Limit: limit}, nil
}
@@ -0,0 +1,33 @@
package logic
import (
"context"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
"github.com/zeromicro/go-zero/core/logx"
)
type GetBadgeConfigDetailLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewGetBadgeConfigDetailLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetBadgeConfigDetailLogic {
return &GetBadgeConfigDetailLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
func (l *GetBadgeConfigDetailLogic) GetBadgeConfigDetail(in *plant.IdReq) (*plant.BadgeConfigInfo, error) {
var badge plantModel.BadgeConfig
if err := l.svcCtx.DB.Where("id = ?", in.Id).First(&badge).Error; err != nil {
return nil, err
}
return &plant.BadgeConfigInfo{
Id: badge.ID, Name: badge.Name, Description: badge.Description, Dimension: badge.Dimension,
GroupId: badge.GroupID, Tier: int32(badge.Tier), TargetAction: badge.TargetAction,
Threshold: badge.Threshold, RewardSunlight: badge.RewardSunlight, IconId: badge.IconID, Sort: int32(badge.Sort),
}, nil
}
@@ -3,6 +3,7 @@ package logic
import (
"context"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
@@ -23,8 +24,45 @@ func NewGetBadgeConfigListLogic(ctx context.Context, svcCtx *svc.ServiceContext)
}
}
// 配置 - 徽章配置列表
func (l *GetBadgeConfigListLogic) GetBadgeConfigList(in *plant.BadgeConfigListReq) (*plant.BadgeConfigListResp, error) {
// todo: add your logic here and delete this line
db := l.svcCtx.DB.Model(&plantModel.BadgeConfig{})
if in.Dimension != "" {
db = db.Where("dimension = ?", in.Dimension)
}
return &plant.BadgeConfigListResp{}, nil
var total int64
db.Count(&total)
pageSize := int(in.PageSize)
if pageSize <= 0 {
pageSize = 20
}
offset := (int(in.Current) - 1) * pageSize
if offset < 0 {
offset = 0
}
var list []plantModel.BadgeConfig
if err := db.Order("dimension asc, group_id asc, tier asc").
Limit(pageSize).Offset(offset).Find(&list).Error; err != nil {
return nil, err
}
var result []*plant.BadgeConfigInfo
for _, item := range list {
result = append(result, &plant.BadgeConfigInfo{
Id: item.ID,
Name: item.Name,
Description: item.Description,
Dimension: item.Dimension,
GroupId: item.GroupID,
Tier: int32(item.Tier),
TargetAction: item.TargetAction,
Threshold: item.Threshold,
RewardSunlight: item.RewardSunlight,
})
}
return &plant.BadgeConfigListResp{List: result, Total: total}, nil
}
@@ -0,0 +1,49 @@
package logic
import (
"context"
"github.com/zeromicro/go-zero/core/logx"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
)
type GetBadgeConfigTreeLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewGetBadgeConfigTreeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetBadgeConfigTreeLogic {
return &GetBadgeConfigTreeLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
func (l *GetBadgeConfigTreeLogic) GetBadgeConfigTree(in *plant.IdReq) (*plant.BadgeConfigTreeResp, error) {
var badges []plantModel.BadgeConfig
if err := l.svcCtx.DB.Order("dimension, group_id, tier asc").Find(&badges).Error; err != nil {
return nil, err
}
// 按 groupId 分组
groupMap := make(map[string]*plant.BadgeGroupInfo)
groupOrder := []string{}
for _, b := range badges {
key := b.GroupID
if key == "" {
key = b.Dimension
}
if _, ok := groupMap[key]; !ok {
groupMap[key] = &plant.BadgeGroupInfo{GroupId: key, Dimension: b.Dimension}
groupOrder = append(groupOrder, key)
}
groupMap[key].Badges = append(groupMap[key].Badges, &plant.BadgeConfigInfo{
Id: b.ID, Name: b.Name, Description: b.Description, Dimension: b.Dimension,
GroupId: b.GroupID, Tier: int32(b.Tier), TargetAction: b.TargetAction,
Threshold: b.Threshold, RewardSunlight: b.RewardSunlight, IconId: b.IconID, Sort: int32(b.Sort),
})
}
groups := make([]*plant.BadgeGroupInfo, 0, len(groupOrder))
for _, k := range groupOrder {
groups = append(groups, groupMap[k])
}
return &plant.BadgeConfigTreeResp{Groups: groups}, nil
}
@@ -0,0 +1,27 @@
package logic
import (
"context"
"github.com/zeromicro/go-zero/core/logx"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
)
type GetExchangeItemDetailLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewGetExchangeItemDetailLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetExchangeItemDetailLogic {
return &GetExchangeItemDetailLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
func (l *GetExchangeItemDetailLogic) GetExchangeItemDetail(in *plant.IdReq) (*plant.ExchangeItemInfo, error) {
var item plantModel.ExchangeItem
if err := l.svcCtx.DB.First(&item, "id = ?", in.Id).Error; err != nil {
return nil, err
}
return exchangeItemInfo(item), nil
}
@@ -3,6 +3,7 @@ package logic
import (
"context"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
@@ -23,9 +24,30 @@ func NewGetExchangeItemListLogic(ctx context.Context, svcCtx *svc.ServiceContext
}
}
// 兑换
// 商品列表(仅上架商品)
func (l *GetExchangeItemListLogic) GetExchangeItemList(in *plant.ExchangeItemListReq) (*plant.ExchangeItemListResp, error) {
// todo: add your logic here and delete this line
db := l.svcCtx.DB.Model(&plantModel.ExchangeItem{}).Where("status = 1")
return &plant.ExchangeItemListResp{}, nil
var total int64
db.Count(&total)
pageSize := int(in.PageSize)
if pageSize <= 0 {
pageSize = 20
}
offset := (int(in.Current) - 1) * pageSize
if offset < 0 {
offset = 0
}
var list []plantModel.ExchangeItem
if err := db.Limit(pageSize).Offset(offset).Order("created_at desc").Find(&list).Error; err != nil {
return nil, err
}
var result []*plant.ExchangeItemInfo
for _, item := range list {
result = append(result, exchangeItemInfo(item))
}
return &plant.ExchangeItemListResp{List: result, Total: total}, nil
}
@@ -0,0 +1,45 @@
package logic
import (
"context"
"github.com/zeromicro/go-zero/core/logx"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
)
type GetExchangeOrderListLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewGetExchangeOrderListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetExchangeOrderListLogic {
return &GetExchangeOrderListLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
func (l *GetExchangeOrderListLogic) GetExchangeOrderList(in *plant.ExchangeOrderListReq) (*plant.ExchangeOrderListResp, error) {
var orders []plantModel.ExchangeOrder
var total int64
db := l.svcCtx.DB.Model(&plantModel.ExchangeOrder{})
if in.UserId != "" {
db = db.Where("user_id = ?", in.UserId)
}
if in.Status > 0 {
db = db.Where("status = ?", in.Status)
}
db.Count(&total)
page, size := in.Current, in.PageSize
if page < 1 {
page = 1
}
if size < 1 {
size = 10
}
db.Offset(int((page - 1) * size)).Limit(int(size)).Order("created_at desc").Find(&orders)
list := make([]*plant.ExchangeOrderInfo, 0, len(orders))
for _, o := range orders {
list = append(list, exchangeOrderInfo(o))
}
return &plant.ExchangeOrderListResp{List: list, Total: total}, nil
}
@@ -0,0 +1,30 @@
package logic
import (
"context"
"github.com/zeromicro/go-zero/core/logx"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
)
type GetLevelConfigDetailLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewGetLevelConfigDetailLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetLevelConfigDetailLogic {
return &GetLevelConfigDetailLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
func (l *GetLevelConfigDetailLogic) GetLevelConfigDetail(in *plant.IdReq) (*plant.LevelConfigInfo, error) {
var lc plantModel.LevelConfig
if err := l.svcCtx.DB.First(&lc, "id = ?", in.Id).Error; err != nil {
return nil, err
}
return &plant.LevelConfigInfo{
Id: lc.ID, Level: int32(lc.Level), Title: lc.Title,
MinSunlight: lc.MinSunlight, Perks: lc.Perks,
}, nil
}
@@ -3,6 +3,7 @@ package logic
import (
"context"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
@@ -23,9 +24,23 @@ func NewGetLevelConfigListLogic(ctx context.Context, svcCtx *svc.ServiceContext)
}
}
// 配置
// 配置 - 等级配置列表
func (l *GetLevelConfigListLogic) GetLevelConfigList(in *plant.PageReq) (*plant.LevelConfigListResp, error) {
// todo: add your logic here and delete this line
var list []plantModel.LevelConfig
if err := l.svcCtx.DB.Order("level asc").Find(&list).Error; err != nil {
return nil, err
}
return &plant.LevelConfigListResp{}, nil
var result []*plant.LevelConfigInfo
for _, item := range list {
result = append(result, &plant.LevelConfigInfo{
Id: item.ID,
Level: int32(item.Level),
Title: item.Title,
MinSunlight: item.MinSunlight,
Perks: item.Perks,
})
}
return &plant.LevelConfigListResp{List: result}, nil
}
@@ -0,0 +1,37 @@
package logic
import (
"context"
"github.com/zeromicro/go-zero/core/logx"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
)
type GetMyBadgesLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewGetMyBadgesLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetMyBadgesLogic {
return &GetMyBadgesLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
func (l *GetMyBadgesLogic) GetMyBadges(in *plant.GetProfileReq) (*plant.UserBadgeListResp, error) {
var userBadges []plantModel.UserBadge
if err := l.svcCtx.DB.Where("user_id = ?", in.UserId).Find(&userBadges).Error; err != nil {
return nil, err
}
list := make([]*plant.UserBadgeInfo, 0, len(userBadges))
for _, ub := range userBadges {
var badge plantModel.BadgeConfig
l.svcCtx.DB.First(&badge, "id = ?", ub.BadgeID)
list = append(list, &plant.UserBadgeInfo{
Id: ub.ID, BadgeId: ub.BadgeID, BadgeName: badge.Name,
Dimension: badge.Dimension, Tier: int32(badge.Tier),
AwardedAt: ub.CreatedAt.Format("2006-01-02 15:04:05"),
})
}
return &plant.UserBadgeListResp{List: list}, nil
}
@@ -0,0 +1,34 @@
package logic
import (
"context"
"github.com/zeromicro/go-zero/core/logx"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
)
type GetMyClassifyLogLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewGetMyClassifyLogLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetMyClassifyLogLogic {
return &GetMyClassifyLogLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
func (l *GetMyClassifyLogLogic) GetMyClassifyLog(in *plant.GetProfileReq) (*plant.ClassifyLogListResp, error) {
var logs []plantModel.OcrLog
if err := l.svcCtx.DB.Where("user_id = ?", in.UserId).Order("created_at desc").Limit(50).Find(&logs).Error; err != nil {
return nil, err
}
list := make([]*plant.ClassifyLogInfo, 0, len(logs))
for _, og := range logs {
list = append(list, &plant.ClassifyLogInfo{
Id: og.ID, UserId: og.UserID, ImageUrl: og.ImageUrl, Result: og.Result,
CreatedAt: og.CreatedAt.Format("2006-01-02 15:04:05"),
})
}
return &plant.ClassifyLogListResp{List: list, Total: int64(len(list))}, nil
}
@@ -0,0 +1,42 @@
package logic
import (
"context"
"github.com/zeromicro/go-zero/core/logx"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
)
type GetMyExchangeOrdersLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewGetMyExchangeOrdersLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetMyExchangeOrdersLogic {
return &GetMyExchangeOrdersLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
func (l *GetMyExchangeOrdersLogic) GetMyExchangeOrders(in *plant.ExchangeOrderListReq) (*plant.ExchangeOrderListResp, error) {
var orders []plantModel.ExchangeOrder
var total int64
db := l.svcCtx.DB.Model(&plantModel.ExchangeOrder{}).Where("user_id = ?", in.UserId)
if in.Status > 0 {
db = db.Where("status = ?", in.Status)
}
db.Count(&total)
page, size := in.Current, in.PageSize
if page < 1 {
page = 1
}
if size < 1 {
size = 10
}
db.Offset(int((page - 1) * size)).Limit(int(size)).Order("created_at desc").Find(&orders)
list := make([]*plant.ExchangeOrderInfo, 0, len(orders))
for _, o := range orders {
list = append(list, exchangeOrderInfo(o))
}
return &plant.ExchangeOrderListResp{List: list, Total: total}, nil
}
@@ -0,0 +1,34 @@
package logic
import (
"context"
"github.com/zeromicro/go-zero/core/logx"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
)
type GetMyStarsLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewGetMyStarsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetMyStarsLogic {
return &GetMyStarsLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
func (l *GetMyStarsLogic) GetMyStars(in *plant.GetProfileReq) (*plant.UserStarListResp, error) {
var stars []plantModel.UserStar
if err := l.svcCtx.DB.Where("user_id = ?", in.UserId).Order("created_at desc").Find(&stars).Error; err != nil {
return nil, err
}
list := make([]*plant.UserStarInfo, 0, len(stars))
for _, s := range stars {
list = append(list, &plant.UserStarInfo{
Id: s.ID, TargetId: s.TargetID, Type: s.Type,
CreatedAt: s.CreatedAt.Format("2006-01-02 15:04:05"),
})
}
return &plant.UserStarListResp{List: list, Total: int64(len(list))}, nil
}
@@ -3,6 +3,7 @@ package logic
import (
"context"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
@@ -23,8 +24,56 @@ func NewGetPlantDetailLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Ge
}
}
// 植物详情
func (l *GetPlantDetailLogic) GetPlantDetail(in *plant.IdReq) (*plant.PlantDetailResp, error) {
// todo: add your logic here and delete this line
var myPlant plantModel.MyPlant
if err := l.svcCtx.DB.Where("id = ?", in.Id).First(&myPlant).Error; err != nil {
return nil, err
}
return &plant.PlantDetailResp{}, nil
// 魏护计划
var carePlans []plantModel.CarePlan
l.svcCtx.DB.Where("plant_id = ?", in.Id).Find(&carePlans)
var planInfos []*plant.CarePlanInfo
for _, cp := range carePlans {
planInfos = append(planInfos, &plant.CarePlanInfo{
Id: cp.ID,
PlantId: cp.PlantID,
Name: cp.Name,
Icon: cp.Icon,
TargetAction: cp.TargetAction,
Period: int32(cp.Period),
})
}
// 成长记录
var growthRecords []plantModel.GrowthRecord
l.svcCtx.DB.Where("plant_id = ?", in.Id).Order("created_at desc").Find(&growthRecords)
var growthInfos []*plant.GrowthRecordInfo
for _, gr := range growthRecords {
growthInfos = append(growthInfos, &plant.GrowthRecordInfo{
Id: gr.ID,
PlantId: gr.PlantID,
Content: gr.Content,
CreatedAt: gr.CreatedAt.Unix(),
})
}
return &plant.PlantDetailResp{
Plant: &plant.PlantInfo{
Id: myPlant.ID,
UserId: myPlant.UserID,
Name: myPlant.Name,
PlantTime: myPlant.PlantTime.Format("2006-01-02"),
Status: int32(myPlant.Status),
Placement: myPlant.Placement,
PotMaterial: myPlant.PotMaterial,
PotSize: myPlant.PotSize,
Sunlight: myPlant.Sunlight,
PlantingMaterial: myPlant.PlantingMaterial,
CreatedAt: myPlant.CreatedAt.Unix(),
},
CarePlans: planInfos,
GrowthRecords: growthInfos,
}, nil
}
@@ -3,6 +3,7 @@ package logic
import (
"context"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
@@ -23,8 +24,46 @@ func NewGetPlantListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetP
}
}
// 我的植物列表
func (l *GetPlantListLogic) GetPlantList(in *plant.PlantListReq) (*plant.PlantListResp, error) {
// todo: add your logic here and delete this line
db := l.svcCtx.DB.Model(&plantModel.MyPlant{}).Where("user_id = ?", in.UserId)
if in.Name != "" {
db = db.Where("name like ?", "%"+in.Name+"%")
}
return &plant.PlantListResp{}, nil
var total int64
db.Count(&total)
pageSize := int(in.PageSize)
if pageSize <= 0 {
pageSize = 20
}
offset := (int(in.Current) - 1) * pageSize
if offset < 0 {
offset = 0
}
var list []plantModel.MyPlant
if err := db.Limit(pageSize).Offset(offset).Order("created_at desc").Find(&list).Error; err != nil {
return nil, err
}
var result []*plant.PlantInfo
for _, item := range list {
result = append(result, &plant.PlantInfo{
Id: item.ID,
UserId: item.UserID,
Name: item.Name,
PlantTime: item.PlantTime.Format("2006-01-02"),
Status: int32(item.Status),
Placement: item.Placement,
PotMaterial: item.PotMaterial,
PotSize: item.PotSize,
Sunlight: item.Sunlight,
PlantingMaterial: item.PlantingMaterial,
CreatedAt: item.CreatedAt.Unix(),
})
}
return &plant.PlantListResp{List: result, Total: total}, nil
}
@@ -3,6 +3,7 @@ package logic
import (
"context"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
@@ -23,8 +24,37 @@ func NewGetPostDetailLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Get
}
}
// 帖子详情
func (l *GetPostDetailLogic) GetPostDetail(in *plant.IdReq) (*plant.PostDetailResp, error) {
// todo: add your logic here and delete this line
var post plantModel.Post
if err := l.svcCtx.DB.Where("id = ?", in.Id).First(&post).Error; err != nil {
return nil, err
}
var comments []plantModel.PostComment
l.svcCtx.DB.Where("post_id = ?", in.Id).Order("created_at asc").Find(&comments)
return &plant.PostDetailResp{}, nil
var commentInfos []*plant.PostCommentInfo
for _, c := range comments {
commentInfos = append(commentInfos, &plant.PostCommentInfo{
Id: c.ID,
UserId: c.UserID,
Content: c.Content,
ParentId: c.ParentID,
CreatedAt: c.CreatedAt.Unix(),
})
}
return &plant.PostDetailResp{
Post: &plant.PostInfo{
Id: post.ID,
UserId: post.UserID,
Title: post.Title,
Content: post.Content,
ViewCount: int32(post.ViewCount),
CommentCount: int32(post.CommentCount),
LikeCount: int32(post.LikeCount),
Location: post.Location,
CreatedAt: post.CreatedAt.Unix(),
},
Comments: commentInfos,
}, nil
}
@@ -3,6 +3,7 @@ package logic
import (
"context"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
@@ -23,8 +24,48 @@ func NewGetPostListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetPo
}
}
// 帖子列表
func (l *GetPostListLogic) GetPostList(in *plant.PostListReq) (*plant.PostListResp, error) {
// todo: add your logic here and delete this line
db := l.svcCtx.DB.Model(&plantModel.Post{})
if in.Keyword != "" {
db = db.Where("title like ? OR content like ?", "%"+in.Keyword+"%", "%"+in.Keyword+"%")
}
if in.UserId != "" {
db = db.Where("user_id = ?", in.UserId)
}
return &plant.PostListResp{}, nil
var total int64
db.Count(&total)
pageSize := int(in.PageSize)
if pageSize <= 0 {
pageSize = 20
}
offset := (int(in.Current) - 1) * pageSize
if offset < 0 {
offset = 0
}
var list []plantModel.Post
if err := db.Limit(pageSize).Offset(offset).Order("created_at desc").Find(&list).Error; err != nil {
return nil, err
}
var result []*plant.PostInfo
for _, item := range list {
result = append(result, &plant.PostInfo{
Id: item.ID,
UserId: item.UserID,
Title: item.Title,
Content: item.Content,
ViewCount: int32(item.ViewCount),
CommentCount: int32(item.CommentCount),
LikeCount: int32(item.LikeCount),
StarCount: int32(item.StarCount),
Location: item.Location,
CreatedAt: item.CreatedAt.Unix(),
})
}
return &plant.PostListResp{List: result, Total: total}, nil
}
@@ -0,0 +1,43 @@
package logic
import (
"context"
"github.com/zeromicro/go-zero/core/logx"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
"time"
)
type GetTodayTaskListLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewGetTodayTaskListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetTodayTaskListLogic {
return &GetTodayTaskListLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
func (l *GetTodayTaskListLogic) GetTodayTaskList(in *plant.GetProfileReq) (*plant.CareTaskListResp, error) {
now := time.Now()
todayStart := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
todayEnd := todayStart.Add(24 * time.Hour)
var tasks []plantModel.CareTask
if err := l.svcCtx.DB.Where(
"user_id = ? AND ((status = 1 AND due_date < ?) OR (status = 2 AND completed_at >= ? AND completed_at < ?))",
in.UserId, todayEnd, todayStart, todayEnd,
).Order("due_date asc").Find(&tasks).Error; err != nil {
return nil, err
}
list := make([]*plant.CareTaskInfo, 0, len(tasks))
for _, t := range tasks {
list = append(list, &plant.CareTaskInfo{
Id: t.ID, PlantId: t.PlantID, PlanId: t.PlanID,
Name: t.Name, Icon: t.Icon, TargetAction: t.TargetAction,
DueDate: t.DueDate.Format("2006-01-02"), Status: int32(t.Status),
})
}
return &plant.CareTaskListResp{List: list, Total: int64(len(list))}, nil
}
@@ -0,0 +1,27 @@
package logic
import (
"context"
"github.com/zeromicro/go-zero/core/logx"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
)
type GetTopicDetailLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewGetTopicDetailLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetTopicDetailLogic {
return &GetTopicDetailLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
func (l *GetTopicDetailLogic) GetTopicDetail(in *plant.IdReq) (*plant.TopicInfo, error) {
var t plantModel.Topic
if err := l.svcCtx.DB.First(&t, "id = ?", in.Id).Error; err != nil {
return nil, err
}
return &plant.TopicInfo{Id: t.ID, Name: t.Name, Icon: t.Icon, Desc: t.Desc, PostCount: int32(t.PostCount)}, nil
}
@@ -2,7 +2,9 @@ package logic
import (
"context"
"time"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
@@ -23,9 +25,32 @@ func NewGetTopicListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetT
}
}
// 话题
// 话题列表
func (l *GetTopicListLogic) GetTopicList(in *plant.IdReq) (*plant.TopicListResp, error) {
// todo: add your logic here and delete this line
return &plant.TopicListResp{}, nil
var list []plantModel.Topic
if err := l.svcCtx.DB.Order("post_count desc").Find(&list).Error; err != nil {
return nil, err
}
var result []*plant.TopicInfo
for _, item := range list {
start, end := "", ""
if item.StartTime != nil {
start = item.StartTime.Format(time.DateTime)
}
if item.EndTime != nil {
end = item.EndTime.Format(time.DateTime)
}
result = append(result, &plant.TopicInfo{
Id: item.ID,
Name: item.Name,
PostCount: int32(item.PostCount),
Icon: item.Icon,
Desc: item.Desc,
Title: item.Title,
Remark: item.Remark,
StartTime: start,
EndTime: end,
})
}
return &plant.TopicListResp{List: result}, nil
}
@@ -3,6 +3,7 @@ package logic
import (
"context"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
@@ -23,9 +24,30 @@ func NewGetUserProfileLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Ge
}
}
// 用户Profile
// 获取用户资料 (首次访问自动初始化)
func (l *GetUserProfileLogic) GetUserProfile(in *plant.GetProfileReq) (*plant.PlantUserProfile, error) {
// todo: add your logic here and delete this line
return &plant.PlantUserProfile{}, nil
var profile plantModel.UserProfile
err := l.svcCtx.DB.Where("user_id = ?", in.UserId).FirstOrCreate(&profile, plantModel.UserProfile{
UserID: in.UserId,
}).Error
if err != nil {
return nil, err
}
return &plant.PlantUserProfile{
Id: profile.ID,
UserId: profile.UserID,
NickName: profile.NickName,
AvatarId: profile.AvatarID,
LevelId: profile.LevelID,
CurrentSunlight: profile.CurrentSunlight,
TotalSunlight: profile.TotalSunlight,
PlantCount: profile.PlantCount,
CareCount: profile.CareCount,
PostCount: profile.PostCount,
WaterCount: profile.WaterCount,
FertilizeCount: profile.FertilizeCount,
RepotCount: profile.RepotCount,
PruneCount: profile.PruneCount,
PhotoCount: profile.PhotoCount,
}, nil
}
@@ -0,0 +1,27 @@
package logic
import (
"context"
"github.com/zeromicro/go-zero/core/logx"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
)
type GetWikiClassDetailLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewGetWikiClassDetailLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetWikiClassDetailLogic {
return &GetWikiClassDetailLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
func (l *GetWikiClassDetailLogic) GetWikiClassDetail(in *plant.IdReq) (*plant.WikiClassInfo, error) {
var wc plantModel.WikiClass
if err := l.svcCtx.DB.First(&wc, "id = ?", in.Id).Error; err != nil {
return nil, err
}
return &plant.WikiClassInfo{Id: wc.ID, Name: wc.Name, OssId: wc.OssID}, nil
}
@@ -3,6 +3,7 @@ package logic
import (
"context"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
@@ -23,8 +24,19 @@ func NewGetWikiClassListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *
}
}
// 百科分类列表
func (l *GetWikiClassListLogic) GetWikiClassList(in *plant.IdReq) (*plant.WikiClassListResp, error) {
// todo: add your logic here and delete this line
return &plant.WikiClassListResp{}, nil
var list []plantModel.WikiClass
if err := l.svcCtx.DB.Order("created_at asc").Find(&list).Error; err != nil {
return nil, err
}
var result []*plant.WikiClassInfo
for _, item := range list {
result = append(result, &plant.WikiClassInfo{
Id: item.ID,
Name: item.Name,
OssId: item.OssID,
})
}
return &plant.WikiClassListResp{List: result}, nil
}
@@ -3,6 +3,7 @@ package logic
import (
"context"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
@@ -23,8 +24,13 @@ func NewGetWikiDetailLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Get
}
}
// 百科详情
func (l *GetWikiDetailLogic) GetWikiDetail(in *plant.IdReq) (*plant.WikiDetailResp, error) {
// todo: add your logic here and delete this line
return &plant.WikiDetailResp{}, nil
var wiki plantModel.Wiki
if err := l.svcCtx.DB.Where("id = ?", in.Id).First(&wiki).Error; err != nil {
return nil, err
}
return &plant.WikiDetailResp{
Wiki: wikiInfoFromModel(l.svcCtx.DB, wiki),
}, nil
}
@@ -3,6 +3,7 @@ package logic
import (
"context"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
@@ -23,9 +24,37 @@ func NewGetWikiListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetWi
}
}
// 百科
// 百科列表
func (l *GetWikiListLogic) GetWikiList(in *plant.WikiListReq) (*plant.WikiListResp, error) {
// todo: add your logic here and delete this line
db := l.svcCtx.DB.Model(&plantModel.Wiki{})
if in.Name != "" {
db = db.Where("name like ?", "%"+in.Name+"%")
}
if in.IsHot > 0 {
db = db.Where("is_hot = ?", in.IsHot)
}
return &plant.WikiListResp{}, nil
var total int64
db.Count(&total)
pageSize := int(in.PageSize)
if pageSize <= 0 {
pageSize = 20
}
offset := (int(in.Current) - 1) * pageSize
if offset < 0 {
offset = 0
}
var list []plantModel.Wiki
if err := db.Limit(pageSize).Offset(offset).Order("created_at desc").Find(&list).Error; err != nil {
return nil, err
}
var result []*plant.WikiInfo
for _, item := range list {
result = append(result, wikiInfoFromModel(l.svcCtx.DB, item))
}
return &plant.WikiListResp{List: result, Total: total}, nil
}
+18 -11
View File
@@ -2,11 +2,12 @@ package logic
import (
"context"
"errors"
"github.com/zeromicro/go-zero/core/logx"
"gorm.io/gorm"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
"github.com/zeromicro/go-zero/core/logx"
)
type LikePostLogic struct {
@@ -16,15 +17,21 @@ type LikePostLogic struct {
}
func NewLikePostLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LikePostLogic {
return &LikePostLogic{
ctx: ctx,
svcCtx: svcCtx,
Logger: logx.WithContext(ctx),
}
return &LikePostLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
func (l *LikePostLogic) LikePost(in *plant.LikePostReq) (*plant.CommonResp, error) {
// todo: add your logic here and delete this line
return &plant.CommonResp{}, nil
var existing plantModel.PostLike
err := l.svcCtx.DB.Where("user_id = ? AND post_id = ?", in.UserId, in.PostId).First(&existing).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
like := plantModel.PostLike{UserID: in.UserId, PostID: in.PostId}
if err2 := l.svcCtx.DB.Create(&like).Error; err2 != nil {
return nil, err2
}
l.svcCtx.DB.Model(&plantModel.Post{}).Where("id = ?", in.PostId).UpdateColumn("like_count", gorm.Expr("like_count + 1"))
} else {
l.svcCtx.DB.Delete(&existing)
l.svcCtx.DB.Model(&plantModel.Post{}).Where("id = ?", in.PostId).UpdateColumn("like_count", gorm.Expr("GREATEST(like_count - 1, 0)"))
}
return &plant.CommonResp{Code: 0, Msg: "ok"}, nil
}
@@ -0,0 +1,48 @@
package logic
import (
"context"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
"github.com/zeromicro/go-zero/core/logx"
)
type MediaCheckCallbackLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewMediaCheckCallbackLogic(ctx context.Context, svcCtx *svc.ServiceContext) *MediaCheckCallbackLogic {
return &MediaCheckCallbackLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
func (l *MediaCheckCallbackLogic) MediaCheckCallback(in *plant.MediaCheckCallbackReq) (*plant.CommonResp, error) {
result := plantModel.MediaCheckResult{
TraceID: in.TraceId,
PostID: in.PostId,
OssID: in.OssId,
UserID: in.UserId,
Status: int(in.Status),
Type: int(in.Type),
ErrMsg: in.ErrMsg,
}
if err := l.svcCtx.DB.Where(plantModel.MediaCheckResult{TraceID: in.TraceId}).
Assign(result).
FirstOrCreate(&plantModel.MediaCheckResult{}).Error; err != nil {
return nil, err
}
if in.PostId != "" {
auditStatus := 1
if in.Status != 0 {
auditStatus = 2
}
_ = l.svcCtx.DB.Model(&plantModel.Post{}).Where("id = ?", in.PostId).Update("has_reviewed", auditStatus).Error
}
return &plant.CommonResp{Code: 0, Msg: "ok"}, nil
}
@@ -0,0 +1,160 @@
package logic
import (
"bytes"
"context"
"crypto/md5"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"strings"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/config"
)
func wikiVectorID(wikiID string) string {
sum := md5.Sum([]byte("sundynix-plant-wiki:" + wikiID))
return hex.EncodeToString(sum[:])
}
func buildWikiVectorText(w plantModel.Wiki) string {
return fmt.Sprintf("植物名字:%s. 拉丁名:%s. 别名:%s. 科属:%s. 分布区域:%s. 生命周期:%s. 生长习性:%s. 繁殖方法:%s. 病虫害:%s. 光照强度:%s. 光照类型:%s. 最佳温度:%s. 茎:%s. 叶型:%s. 叶色:%s. 叶形:%s. 高度:%d厘米. 开花期:%s. 花色:%s. 花形:%s. 花直径:%d厘米. 果实:%s.",
w.Name, w.LatinName, w.Aliases, w.Genus, w.DistributionArea, w.LifeCycle,
w.GrowthHabit, w.ReproductionMethod, w.PestsDiseases, w.LightIntensity,
w.LightType, w.OptimalTempPeriod, w.Stem, w.FoliageType, w.FoliageColor,
w.FoliageShape, w.Height, w.FloweringPeriod, w.FloweringColor,
w.FloweringShape, w.FlowerDiameter, w.Fruit)
}
func embeddingModel(c config.Config) string {
if c.Ai.EmbeddingModelName != "" {
return c.Ai.EmbeddingModelName
}
return "text-embedding-3-small"
}
func createEmbedding(ctx context.Context, c config.Config, text string) ([]float32, error) {
body, _ := json.Marshal(map[string]interface{}{
"model": embeddingModel(c),
"input": text,
})
req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.Ai.EmbeddingApiUrl, bytes.NewReader(body))
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+c.Ai.EmbeddingApiKey)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
raw, _ := io.ReadAll(io.LimitReader(resp.Body, 4096))
return nil, fmt.Errorf("embedding 请求失败: %s %s", resp.Status, strings.TrimSpace(string(raw)))
}
var parsed struct {
Data []struct {
Embedding []float32 `json:"embedding"`
} `json:"data"`
}
if err := json.NewDecoder(resp.Body).Decode(&parsed); err != nil {
return nil, err
}
if len(parsed.Data) == 0 || len(parsed.Data[0].Embedding) == 0 {
return nil, errors.New("embedding 响应为空")
}
return parsed.Data[0].Embedding, nil
}
func qdrantURL(c config.Config, path string) string {
return strings.TrimRight(c.Ai.QdrantUrl, "/") + path
}
func doQdrant(ctx context.Context, c config.Config, method, path string, body interface{}) error {
var reader io.Reader
if body != nil {
raw, _ := json.Marshal(body)
reader = bytes.NewReader(raw)
}
req, err := http.NewRequestWithContext(ctx, method, qdrantURL(c, path), reader)
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")
if c.Ai.QdrantApiKey != "" {
req.Header.Set("api-key", c.Ai.QdrantApiKey)
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
raw, _ := io.ReadAll(io.LimitReader(resp.Body, 4096))
return fmt.Errorf("qdrant 请求失败: %s %s", resp.Status, strings.TrimSpace(string(raw)))
}
return nil
}
func ensureQdrantCollection(ctx context.Context, c config.Config, dim int) error {
getReq, err := http.NewRequestWithContext(ctx, http.MethodGet, qdrantURL(c, "/collections/"+c.Ai.QdrantCollection), nil)
if err != nil {
return err
}
if c.Ai.QdrantApiKey != "" {
getReq.Header.Set("api-key", c.Ai.QdrantApiKey)
}
if resp, err := http.DefaultClient.Do(getReq); err == nil {
_ = resp.Body.Close()
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
return nil
}
}
if dim <= 0 {
dim = c.Ai.VectorDimension
}
if dim <= 0 {
dim = 1536
}
return doQdrant(ctx, c, http.MethodPut, "/collections/"+c.Ai.QdrantCollection, map[string]interface{}{
"vectors": map[string]interface{}{
"size": dim,
"distance": "Cosine",
},
})
}
func upsertWikiVector(ctx context.Context, c config.Config, w plantModel.Wiki) error {
text := buildWikiVectorText(w)
vector, err := createEmbedding(ctx, c, text)
if err != nil {
return err
}
if err := ensureQdrantCollection(ctx, c, len(vector)); err != nil {
return err
}
return doQdrant(ctx, c, http.MethodPut, "/collections/"+c.Ai.QdrantCollection+"/points?wait=true", map[string]interface{}{
"points": []map[string]interface{}{
{
"id": wikiVectorID(w.ID),
"vector": vector,
"payload": map[string]interface{}{
"wiki_id": w.ID,
"name": w.Name,
"full_text": text,
},
},
},
})
}
func deleteWikiVector(ctx context.Context, c config.Config, wikiID string) error {
return doQdrant(ctx, c, http.MethodPost, "/collections/"+c.Ai.QdrantCollection+"/points/delete?wait=true", map[string]interface{}{
"points": []string{wikiVectorID(wikiID)},
})
}
@@ -0,0 +1,35 @@
package logic
import (
"context"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
"github.com/zeromicro/go-zero/core/logx"
)
type SaveAiChatHistoryLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewSaveAiChatHistoryLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SaveAiChatHistoryLogic {
return &SaveAiChatHistoryLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
func (l *SaveAiChatHistoryLogic) SaveAiChatHistory(in *plant.SaveAiChatHistoryReq) (*plant.CommonResp, error) {
history := plantModel.AiChatHistory{
UserID: in.UserId,
Question: in.Question,
Answer: in.Answer,
Role: "assistant",
Content: in.Answer,
}
if err := l.svcCtx.DB.Create(&history).Error; err != nil {
return nil, err
}
return &plant.CommonResp{Code: 0, Msg: history.ID}, nil
}
@@ -0,0 +1,39 @@
package logic
import (
"context"
"errors"
"github.com/zeromicro/go-zero/core/logx"
"gorm.io/gorm"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
)
type StarPostLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewStarPostLogic(ctx context.Context, svcCtx *svc.ServiceContext) *StarPostLogic {
return &StarPostLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
func (l *StarPostLogic) StarPost(in *plant.LikePostReq) (*plant.CommonResp, error) {
var existing plantModel.UserStar
err := l.svcCtx.DB.Where("user_id = ? AND target_id = ? AND type = ?", in.UserId, in.PostId, "post").First(&existing).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
// 新增收藏
star := plantModel.UserStar{UserID: in.UserId, TargetID: in.PostId, Type: "post"}
if err2 := l.svcCtx.DB.Create(&star).Error; err2 != nil {
return nil, err2
}
l.svcCtx.DB.Model(&plantModel.Post{}).Where("id = ?", in.PostId).UpdateColumn("star_count", gorm.Expr("star_count + 1"))
} else {
// 取消收藏
l.svcCtx.DB.Delete(&existing)
l.svcCtx.DB.Model(&plantModel.Post{}).Where("id = ?", in.PostId).UpdateColumn("star_count", gorm.Expr("GREATEST(star_count - 1, 0)"))
}
return &plant.CommonResp{Code: 0, Msg: "ok"}, nil
}
@@ -0,0 +1,43 @@
package logic
import (
"context"
"errors"
"fmt"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
"github.com/zeromicro/go-zero/core/logx"
)
type SyncAllWikiVectorLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewSyncAllWikiVectorLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SyncAllWikiVectorLogic {
return &SyncAllWikiVectorLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
func (l *SyncAllWikiVectorLogic) SyncAllWikiVector(in *plant.PageReq) (*plant.CommonResp, error) {
if l.svcCtx.Config.Ai.EmbeddingApiUrl == "" || l.svcCtx.Config.Ai.QdrantUrl == "" || l.svcCtx.Config.Ai.QdrantCollection == "" {
return nil, errors.New("AI/RAG 未配置 EmbeddingApiUrl、QdrantUrl 或 QdrantCollection")
}
var wikis []plantModel.Wiki
if err := l.svcCtx.DB.Find(&wikis).Error; err != nil {
return nil, err
}
success := 0
for _, wiki := range wikis {
if err := upsertWikiVector(l.ctx, l.svcCtx.Config, wiki); err != nil {
l.Logger.Errorf("sync wiki vector failed, wiki_id=%s, err=%v", wiki.ID, err)
continue
}
success++
_ = l.svcCtx.DB.Model(&plantModel.Wiki{}).Where("id = ?", wiki.ID).Update("is_vector_synced", true).Error
}
return &plant.CommonResp{Code: 0, Msg: fmt.Sprintf("同步完成: %d/%d", success, len(wikis))}, nil
}
@@ -0,0 +1,42 @@
package logic
import (
"context"
"errors"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
"github.com/zeromicro/go-zero/core/logx"
)
type SyncWikiVectorLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewSyncWikiVectorLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SyncWikiVectorLogic {
return &SyncWikiVectorLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
func (l *SyncWikiVectorLogic) SyncWikiVector(in *plant.SyncWikiVectorReq) (*plant.CommonResp, error) {
if in.WikiId == "" {
return nil, errors.New("wikiId 不能为空")
}
if l.svcCtx.Config.Ai.EmbeddingApiUrl == "" || l.svcCtx.Config.Ai.QdrantUrl == "" || l.svcCtx.Config.Ai.QdrantCollection == "" {
return nil, errors.New("AI/RAG 未配置 EmbeddingApiUrl、QdrantUrl 或 QdrantCollection")
}
var wiki plantModel.Wiki
if err := l.svcCtx.DB.Where("id = ?", in.WikiId).First(&wiki).Error; err != nil {
return nil, err
}
if err := upsertWikiVector(l.ctx, l.svcCtx.Config, wiki); err != nil {
return nil, err
}
if err := l.svcCtx.DB.Model(&plantModel.Wiki{}).Where("id = ?", in.WikiId).Update("is_vector_synced", true).Error; err != nil {
return nil, err
}
return &plant.CommonResp{Code: 0, Msg: "ok"}, nil
}
@@ -2,11 +2,14 @@ package logic
import (
"context"
"errors"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
"github.com/zeromicro/go-zero/core/logx"
"gorm.io/gorm"
)
type ToggleWikiStarLogic struct {
@@ -23,8 +26,28 @@ func NewToggleWikiStarLogic(ctx context.Context, svcCtx *svc.ServiceContext) *To
}
}
// 收藏/取消收藏百科
func (l *ToggleWikiStarLogic) ToggleWikiStar(in *plant.ToggleStarReq) (*plant.CommonResp, error) {
// todo: add your logic here and delete this line
return &plant.CommonResp{}, nil
var star plantModel.UserStar
err := l.svcCtx.DB.Where("user_id = ? AND target_id = ? AND type = ?", in.UserId, in.TargetId, in.Type).First(&star).Error
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
return nil, err
}
if errors.Is(err, gorm.ErrRecordNotFound) {
// 未收藏 → 创建
newStar := plantModel.UserStar{
UserID: in.UserId,
TargetID: in.TargetId,
Type: in.Type,
}
if err := l.svcCtx.DB.Create(&newStar).Error; err != nil {
return nil, err
}
return &plant.CommonResp{Code: 0, Msg: "starred"}, nil
}
// 已收藏 → 取消
if err := l.svcCtx.DB.Unscoped().Delete(&star).Error; err != nil {
return nil, err
}
return &plant.CommonResp{Code: 0, Msg: "unstarred"}, nil
}
@@ -0,0 +1,32 @@
package logic
import (
"context"
"github.com/zeromicro/go-zero/core/logx"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
)
type UpdateBadgeConfigLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewUpdateBadgeConfigLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateBadgeConfigLogic {
return &UpdateBadgeConfigLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
func (l *UpdateBadgeConfigLogic) UpdateBadgeConfig(in *plant.UpdateBadgeConfigReq) (*plant.CommonResp, error) {
updates := map[string]interface{}{
"name": in.Name, "description": in.Description, "dimension": in.Dimension,
"group_id": in.GroupId, "tier": in.Tier, "target_action": in.TargetAction,
"threshold": in.Threshold, "reward_sunlight": in.RewardSunlight,
"icon_id": in.IconId, "sort": in.Sort,
}
if err := l.svcCtx.DB.Model(&plantModel.BadgeConfig{}).Where("id = ?", in.Id).Updates(updates).Error; err != nil {
return nil, err
}
return &plant.CommonResp{Code: 0, Msg: "ok"}, nil
}
@@ -0,0 +1,51 @@
package logic
import (
"context"
"time"
"github.com/zeromicro/go-zero/core/logx"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
)
type UpdateExchangeItemLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewUpdateExchangeItemLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateExchangeItemLogic {
return &UpdateExchangeItemLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
func (l *UpdateExchangeItemLogic) UpdateExchangeItem(in *plant.UpdateExchangeItemReq) (*plant.CommonResp, error) {
cost := in.Cost
if in.CostSunlight > 0 {
cost = in.CostSunlight
}
imgID := in.ImgId
if imgID == "" {
imgID = in.ImageId
}
updates := map[string]interface{}{
"name": in.Name, "desc": in.Desc, "description": in.Desc, "img_id": imgID, "image_id": imgID,
"cost": cost, "cost_sunlight": cost, "stock": in.Stock, "status": in.Status,
"type": in.Type, "limit_per_user": in.LimitPerUser, "sort": in.Sort,
}
if in.StartTime != "" {
if t, err := time.Parse(time.DateTime, in.StartTime); err == nil {
updates["start_time"] = &t
}
}
if in.EndTime != "" {
if t, err := time.Parse(time.DateTime, in.EndTime); err == nil {
updates["end_time"] = &t
}
}
if err := l.svcCtx.DB.Model(&plantModel.ExchangeItem{}).Where("id = ?", in.Id).Updates(updates).Error; err != nil {
return nil, err
}
return &plant.CommonResp{Code: 0, Msg: "ok"}, nil
}
@@ -0,0 +1,65 @@
package logic
import (
"context"
"errors"
"github.com/zeromicro/go-zero/core/logx"
"gorm.io/gorm"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
"time"
)
type UpdateExchangeOrderLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewUpdateExchangeOrderLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateExchangeOrderLogic {
return &UpdateExchangeOrderLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
func (l *UpdateExchangeOrderLogic) UpdateExchangeOrder(in *plant.UpdateExchangeOrderReq) (*plant.CommonResp, error) {
updateMap := map[string]interface{}{"status": int(in.Status), "tracking_no": in.TrackingNo, "remark": in.Remark}
if in.Status == 4 {
now := time.Now()
updateMap["completed_at"] = &now
}
if in.Status == 5 {
err := l.svcCtx.DB.Transaction(func(tx *gorm.DB) error {
var order plantModel.ExchangeOrder
if err := tx.Set("gorm:query_option", "FOR UPDATE").Where("id = ?", in.Id).First(&order).Error; err != nil {
return err
}
if order.Status == 5 {
return errors.New("订单已取消")
}
cost := order.CostSunlight
if cost == 0 {
cost = order.Cost
}
if err := tx.Model(&plantModel.UserProfile{}).Where("user_id = ?", order.UserID).
Update("current_sunlight", gorm.Expr("current_sunlight + ?", cost)).Error; err != nil {
return err
}
if order.Quantity <= 0 {
order.Quantity = 1
}
if err := tx.Model(&plantModel.ExchangeItem{}).Where("id = ? AND stock >= 0", order.ItemID).
Update("stock", gorm.Expr("stock + ?", order.Quantity)).Error; err != nil {
return err
}
return tx.Model(&plantModel.ExchangeOrder{}).Where("id = ?", in.Id).Updates(updateMap).Error
})
if err != nil {
return nil, err
}
return &plant.CommonResp{Code: 0, Msg: "ok"}, nil
}
if err := l.svcCtx.DB.Model(&plantModel.ExchangeOrder{}).Where("id = ?", in.Id).Updates(updateMap).Error; err != nil {
return nil, err
}
return &plant.CommonResp{Code: 0, Msg: "ok"}, nil
}
@@ -0,0 +1,27 @@
package logic
import (
"context"
"github.com/zeromicro/go-zero/core/logx"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
)
type UpdateLevelConfigLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewUpdateLevelConfigLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateLevelConfigLogic {
return &UpdateLevelConfigLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
func (l *UpdateLevelConfigLogic) UpdateLevelConfig(in *plant.UpdateLevelConfigReq) (*plant.CommonResp, error) {
updates := map[string]interface{}{"level": in.Level, "title": in.Title, "min_sunlight": in.MinSunlight, "perks": in.Perks}
if err := l.svcCtx.DB.Model(&plantModel.LevelConfig{}).Where("id = ?", in.Id).Updates(updates).Error; err != nil {
return nil, err
}
return &plant.CommonResp{Code: 0, Msg: "ok"}, nil
}
@@ -3,6 +3,7 @@ package logic
import (
"context"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
@@ -23,8 +24,18 @@ func NewUpdatePlantLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Updat
}
}
// 更新植物
func (l *UpdatePlantLogic) UpdatePlant(in *plant.UpdatePlantReq) (*plant.CommonResp, error) {
// todo: add your logic here and delete this line
return &plant.CommonResp{}, nil
updateMap := map[string]interface{}{
"name": in.Name,
"placement": in.Placement,
"pot_material": in.PotMaterial,
"pot_size": in.PotSize,
"sunlight": in.Sunlight,
"planting_material": in.PlantingMaterial,
}
if err := l.svcCtx.DB.Model(&plantModel.MyPlant{}).Where("id = ?", in.Id).Updates(updateMap).Error; err != nil {
return nil, err
}
return &plant.CommonResp{Code: 0, Msg: "ok"}, nil
}
@@ -0,0 +1,43 @@
package logic
import (
"context"
"time"
"github.com/zeromicro/go-zero/core/logx"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
)
type UpdateTopicLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewUpdateTopicLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateTopicLogic {
return &UpdateTopicLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
func (l *UpdateTopicLogic) UpdateTopic(in *plant.UpdateTopicReq) (*plant.CommonResp, error) {
name := in.Name
if name == "" {
name = in.Title
}
updates := map[string]interface{}{"name": name, "title": in.Title, "icon": in.Icon, "desc": in.Desc, "remark": in.Remark}
if in.StartTime != "" {
if t, err := time.Parse(time.DateTime, in.StartTime); err == nil {
updates["start_time"] = &t
}
}
if in.EndTime != "" {
if t, err := time.Parse(time.DateTime, in.EndTime); err == nil {
updates["end_time"] = &t
}
}
if err := l.svcCtx.DB.Model(&plantModel.Topic{}).Where("id = ?", in.Id).Updates(updates).Error; err != nil {
return nil, err
}
return &plant.CommonResp{Code: 0, Msg: "ok"}, nil
}
@@ -3,6 +3,7 @@ package logic
import (
"context"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
@@ -23,8 +24,21 @@ func NewUpdateUserProfileLogic(ctx context.Context, svcCtx *svc.ServiceContext)
}
}
// 更新用户资料
func (l *UpdateUserProfileLogic) UpdateUserProfile(in *plant.UpdateProfileReq) (*plant.CommonResp, error) {
// todo: add your logic here and delete this line
return &plant.CommonResp{}, nil
updateMap := map[string]interface{}{}
if in.NickName != "" {
updateMap["nick_name"] = in.NickName
}
if in.AvatarId != "" {
updateMap["avatar_id"] = in.AvatarId
}
if len(updateMap) == 0 {
return &plant.CommonResp{Code: 0, Msg: "nothing to update"}, nil
}
if err := l.svcCtx.DB.Model(&plantModel.UserProfile{}).Where("user_id = ?", in.UserId).
Updates(updateMap).Error; err != nil {
return nil, err
}
return &plant.CommonResp{Code: 0, Msg: "ok"}, nil
}
@@ -0,0 +1,27 @@
package logic
import (
"context"
"github.com/zeromicro/go-zero/core/logx"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
)
type UpdateWikiClassLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewUpdateWikiClassLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateWikiClassLogic {
return &UpdateWikiClassLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
func (l *UpdateWikiClassLogic) UpdateWikiClass(in *plant.UpdateWikiClassReq) (*plant.CommonResp, error) {
updates := map[string]interface{}{"name": in.Name, "oss_id": in.Icon}
if err := l.svcCtx.DB.Model(&plantModel.WikiClass{}).Where("id = ?", in.Id).Updates(updates).Error; err != nil {
return nil, err
}
return &plant.CommonResp{Code: 0, Msg: "ok"}, nil
}
@@ -0,0 +1,53 @@
package logic
import (
"context"
"github.com/zeromicro/go-zero/core/logx"
"gorm.io/gorm"
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/internal/svc"
"sundynix-micro-go/app/plant/rpc/plant"
)
type UpdateWikiLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewUpdateWikiLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateWikiLogic {
return &UpdateWikiLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
}
func (l *UpdateWikiLogic) UpdateWiki(in *plant.UpdateWikiReq) (*plant.CommonResp, error) {
classID := in.ClassId
if classID == "" && len(in.ClassIds) > 0 {
classID = in.ClassIds[0]
}
updates := map[string]interface{}{
"name": in.Name, "latin_name": in.LatinName, "aliases": in.Aliases,
"genus": in.Genus, "difficulty": in.Difficulty, "is_hot": in.IsHot,
"growth_habit": in.GrowthHabit, "light_intensity": in.LightIntensity,
"optimal_temp_period": in.OptimalTempPeriod, "class_id": classID,
"distribution_area": in.DistributionArea, "life_cycle": in.LifeCycle,
"reproduction_method": in.ReproductionMethod, "pests_diseases": in.PestsDiseases,
"light_type": in.LightType, "stem": in.Stem, "fruit": in.Fruit,
"foliage_type": in.FoliageType, "foliage_color": in.FoliageColor,
"foliage_shape": in.FoliageShape, "height": in.Height,
"flowering_period": in.FloweringPeriod, "flowering_color": in.FloweringColor,
"flowering_shape": in.FloweringShape, "flower_diameter": in.FloweringDiameter,
"is_vector_synced": false,
}
if err := l.svcCtx.DB.Transaction(func(tx *gorm.DB) error {
if err := tx.Model(&plantModel.Wiki{}).Where("id = ?", in.Id).Updates(updates).Error; err != nil {
return err
}
return replaceWikiRelations(tx, in.Id, in.ClassIds, in.OssIds, in.RelatedWikiIds)
}); err != nil {
return nil, err
}
go func() {
_, _ = NewSyncWikiVectorLogic(context.Background(), l.svcCtx).SyncWikiVector(&plant.SyncWikiVectorReq{WikiId: in.Id})
}()
return &plant.CommonResp{Code: 0, Msg: "ok"}, nil
}
@@ -0,0 +1,104 @@
package logic
import (
plantModel "sundynix-micro-go/app/plant/model"
"sundynix-micro-go/app/plant/rpc/plant"
"gorm.io/gorm"
)
func wikiRelationIDs(db *gorm.DB, wikiID string) (classIDs, ossIDs, relatedWikiIDs []string) {
var classes []plantModel.WikiClassRelation
_ = db.Where("wiki_id = ?", wikiID).Find(&classes).Error
for _, item := range classes {
classIDs = append(classIDs, item.ClassID)
}
var ossList []plantModel.WikiOss
_ = db.Where("wiki_id = ?", wikiID).Find(&ossList).Error
for _, item := range ossList {
ossIDs = append(ossIDs, item.OssID)
}
var related []plantModel.WikiRelated
_ = db.Where("wiki_id = ?", wikiID).Find(&related).Error
for _, item := range related {
relatedWikiIDs = append(relatedWikiIDs, item.RelatedWikiID)
}
return
}
func wikiInfoFromModel(db *gorm.DB, item plantModel.Wiki) *plant.WikiInfo {
classIDs, ossIDs, relatedWikiIDs := wikiRelationIDs(db, item.ID)
return &plant.WikiInfo{
Id: item.ID,
Name: item.Name,
LatinName: item.LatinName,
Aliases: item.Aliases,
Genus: item.Genus,
Difficulty: int32(item.Difficulty),
IsHot: int32(item.IsHot),
GrowthHabit: item.GrowthHabit,
LightIntensity: item.LightIntensity,
OptimalTempPeriod: item.OptimalTempPeriod,
CreatedAt: item.CreatedAt.Unix(),
ClassId: item.ClassID,
DistributionArea: item.DistributionArea,
LifeCycle: item.LifeCycle,
ClassIds: classIDs,
OssIds: ossIDs,
RelatedWikiIds: relatedWikiIDs,
IsVectorSynced: item.IsVectorSynced == 1,
ReproductionMethod: item.ReproductionMethod,
PestsDiseases: item.PestsDiseases,
LightType: item.LightType,
Stem: item.Stem,
Fruit: item.Fruit,
FoliageType: item.FoliageType,
FoliageColor: item.FoliageColor,
FoliageShape: item.FoliageShape,
Height: int32(item.Height),
FloweringPeriod: item.FloweringPeriod,
FloweringColor: item.FloweringColor,
FloweringShape: item.FloweringShape,
FloweringDiameter: int32(item.FlowerDiameter),
}
}
func replaceWikiRelations(tx *gorm.DB, wikiID string, classIDs, ossIDs, relatedWikiIDs []string) error {
if err := tx.Where("wiki_id = ?", wikiID).Delete(&plantModel.WikiClassRelation{}).Error; err != nil {
return err
}
if err := tx.Where("wiki_id = ?", wikiID).Delete(&plantModel.WikiOss{}).Error; err != nil {
return err
}
if err := tx.Where("wiki_id = ?", wikiID).Delete(&plantModel.WikiRelated{}).Error; err != nil {
return err
}
for _, classID := range classIDs {
if classID == "" {
continue
}
if err := tx.Create(&plantModel.WikiClassRelation{WikiID: wikiID, ClassID: classID}).Error; err != nil {
return err
}
}
for _, ossID := range ossIDs {
if ossID == "" {
continue
}
if err := tx.Create(&plantModel.WikiOss{WikiID: wikiID, OssID: ossID}).Error; err != nil {
return err
}
}
for _, relatedID := range relatedWikiIDs {
if relatedID == "" || relatedID == wikiID {
continue
}
if err := tx.Create(&plantModel.WikiRelated{WikiID: wikiID, RelatedWikiID: relatedID}).Error; err != nil {
return err
}
}
return nil
}