feat: Add callback and exchange features, refactor care plan task generation logic, and update wiki ordering to prioritize hot items.
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
package plant
|
||||
|
||||
import (
|
||||
"sundynix-go/global"
|
||||
"sundynix-go/model/commom/response"
|
||||
plantRes "sundynix-go/model/plant/response"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type CallbackApi struct{}
|
||||
|
||||
// MediaCheckCallback 微信内容安全回调接口
|
||||
func (a *CallbackApi) MediaCheckCallback(c *gin.Context) {
|
||||
var cb plantRes.WeChatCheckResultCallback
|
||||
if err := c.ShouldBindJSON(&cb); err != nil {
|
||||
global.Logger.Error("解析微信回调JSON失败", zap.Error(err))
|
||||
response.FailWithMsg("解析失败", c)
|
||||
return
|
||||
}
|
||||
|
||||
if err := callbackService.HandleMediaCheckCallback(cb); err != nil {
|
||||
global.Logger.Error("处理微信回调失败", zap.Error(err))
|
||||
response.FailWithMsg("处理失败", c)
|
||||
return
|
||||
}
|
||||
|
||||
response.OkWithMsg("success", c)
|
||||
}
|
||||
@@ -13,6 +13,7 @@ type ApiGroup struct {
|
||||
UserProfileApi
|
||||
BadgeConfigApi
|
||||
CallbackApi
|
||||
ExchangeApi
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -26,4 +27,5 @@ var (
|
||||
userProfileService = service.GroupApp.PlantServiceGroup.UserProfileService
|
||||
badgeConfigService = service.GroupApp.PlantServiceGroup.BadgeConfigService
|
||||
callbackService = service.GroupApp.PlantServiceGroup.CallbackService
|
||||
exchangeService = service.GroupApp.PlantServiceGroup.ExchangeService
|
||||
)
|
||||
|
||||
@@ -0,0 +1,275 @@
|
||||
package plant
|
||||
|
||||
import (
|
||||
"sundynix-go/global"
|
||||
"sundynix-go/model/commom/response"
|
||||
"sundynix-go/model/plant/request"
|
||||
"sundynix-go/utils/auth"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type ExchangeApi struct{}
|
||||
|
||||
// ==================== 管理端 API ====================
|
||||
|
||||
// CreateItem 创建兑换商品
|
||||
// @Tags 兑换中心-管理
|
||||
// @Summary 创建兑换商品
|
||||
// @Security BearerAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.ExchangeItemCreateReq true "创建兑换商品"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}"
|
||||
// @Router /exchange/item/create [post]
|
||||
func (a *ExchangeApi) CreateItem(c *gin.Context) {
|
||||
var req request.ExchangeItemCreateReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.FailWithMsg("请求参数错误", c)
|
||||
return
|
||||
}
|
||||
if err := exchangeService.CreateItem(req); err != nil {
|
||||
global.Logger.Error("创建兑换商品失败", zap.Error(err))
|
||||
response.FailWithMsg("创建失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMsg("创建成功", c)
|
||||
}
|
||||
|
||||
// UpdateItem 更新兑换商品
|
||||
// @Tags 兑换中心-管理
|
||||
// @Summary 更新兑换商品
|
||||
// @Security BearerAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.ExchangeItemUpdateReq true "更新兑换商品"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"更新成功"}"
|
||||
// @Router /exchange/item/update [post]
|
||||
func (a *ExchangeApi) UpdateItem(c *gin.Context) {
|
||||
var req request.ExchangeItemUpdateReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.FailWithMsg("请求参数错误", c)
|
||||
return
|
||||
}
|
||||
if err := exchangeService.UpdateItem(req); err != nil {
|
||||
global.Logger.Error("更新兑换商品失败", zap.Error(err))
|
||||
response.FailWithMsg("更新失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMsg("更新成功", c)
|
||||
}
|
||||
|
||||
// DeleteItem 删除兑换商品
|
||||
// @Tags 兑换中心-管理
|
||||
// @Summary 删除兑换商品
|
||||
// @Security BearerAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.ExchangeItemCreateReq true "删除兑换商品"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}"
|
||||
// @Router /exchange/item/delete [post]
|
||||
func (a *ExchangeApi) DeleteItem(c *gin.Context) {
|
||||
var req struct {
|
||||
Id string `json:"id" binding:"required"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.FailWithMsg("请求参数错误", c)
|
||||
return
|
||||
}
|
||||
if err := exchangeService.DeleteItem(req.Id); err != nil {
|
||||
global.Logger.Error("删除兑换商品失败", zap.Error(err))
|
||||
response.FailWithMsg("删除失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMsg("删除成功", c)
|
||||
}
|
||||
|
||||
// AdminItemList 管理端商品列表
|
||||
// @Tags 兑换中心-管理
|
||||
// @Summary 管理端商品列表
|
||||
// @Security BearerAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.ExchangeItemListReq true "分页获取商品列表"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"查询成功"}"
|
||||
// @Router /exchange/item/list [post]
|
||||
func (a *ExchangeApi) AdminItemList(c *gin.Context) {
|
||||
var req request.ExchangeItemListReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.FailWithMsg("请求参数错误", c)
|
||||
return
|
||||
}
|
||||
list, total, err := exchangeService.AdminItemList(req)
|
||||
if err != nil {
|
||||
global.Logger.Error("获取商品列表失败", zap.Error(err))
|
||||
response.FailWithMsg("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithData(response.PageResult{
|
||||
List: list,
|
||||
Total: total,
|
||||
Page: req.Current,
|
||||
PageSize: req.PageSize,
|
||||
}, c)
|
||||
}
|
||||
|
||||
// AdminOrderList 管理端订单列表
|
||||
// @Tags 兑换中心-管理
|
||||
// @Summary 管理端订单列表
|
||||
// @Security BearerAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.ExchangeOrderListReq true "分页获取订单列表"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"查询成功"}"
|
||||
// @Router /exchange/order/list [post]
|
||||
func (a *ExchangeApi) AdminOrderList(c *gin.Context) {
|
||||
var req request.ExchangeOrderListReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.FailWithMsg("请求参数错误", c)
|
||||
return
|
||||
}
|
||||
list, total, err := exchangeService.AdminOrderList(req)
|
||||
if err != nil {
|
||||
global.Logger.Error("获取订单列表失败", zap.Error(err))
|
||||
response.FailWithMsg("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithData(response.PageResult{
|
||||
List: list,
|
||||
Total: total,
|
||||
Page: req.Current,
|
||||
PageSize: req.PageSize,
|
||||
}, c)
|
||||
}
|
||||
|
||||
// UpdateOrderStatus 更新订单状态
|
||||
// @Tags 兑换中心-管理
|
||||
// @Summary 更新订单状态
|
||||
// @Security BearerAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.ExchangeOrderUpdateReq true "更新订单状态"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"更新成功"}"
|
||||
// @Router /exchange/order/update [post]
|
||||
func (a *ExchangeApi) UpdateOrderStatus(c *gin.Context) {
|
||||
var req request.ExchangeOrderUpdateReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.FailWithMsg("请求参数错误", c)
|
||||
return
|
||||
}
|
||||
if err := exchangeService.UpdateOrderStatus(req); err != nil {
|
||||
global.Logger.Error("更新订单状态失败", zap.Error(err))
|
||||
response.FailWithMsg(err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMsg("更新成功", c)
|
||||
}
|
||||
|
||||
// ==================== 用户端 API ====================
|
||||
|
||||
// UserItemList 用户端商品列表
|
||||
// @Tags 兑换中心-用户
|
||||
// @Summary 用户端商品列表
|
||||
// @Security BearerAuth
|
||||
// @Produce application/json
|
||||
// @Param current query int false "页码"
|
||||
// @Param pageSize query int false "每页大小"
|
||||
// @Param type query string false "商品类型"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"查询成功"}"
|
||||
// @Router /exchange/list [get]
|
||||
func (a *ExchangeApi) UserItemList(c *gin.Context) {
|
||||
var req request.ExchangeItemListReq
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
response.FailWithMsg("请求参数错误", c)
|
||||
return
|
||||
}
|
||||
list, total, err := exchangeService.UserItemList(req)
|
||||
if err != nil {
|
||||
global.Logger.Error("获取商品列表失败", zap.Error(err))
|
||||
response.FailWithMsg("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithData(response.PageResult{
|
||||
List: list,
|
||||
Total: total,
|
||||
Page: req.Current,
|
||||
PageSize: req.PageSize,
|
||||
}, c)
|
||||
}
|
||||
|
||||
// UserItemDetail 商品详情
|
||||
// @Tags 兑换中心-用户
|
||||
// @Summary 商品详情
|
||||
// @Security BearerAuth
|
||||
// @Produce application/json
|
||||
// @Param id query string true "商品ID"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"查询成功"}"
|
||||
// @Router /exchange/detail [get]
|
||||
func (a *ExchangeApi) UserItemDetail(c *gin.Context) {
|
||||
itemId := c.Query("id")
|
||||
if itemId == "" {
|
||||
response.FailWithMsg("参数错误", c)
|
||||
return
|
||||
}
|
||||
item, err := exchangeService.UserItemDetail(itemId)
|
||||
if err != nil {
|
||||
response.FailWithMsg("商品不存在", c)
|
||||
return
|
||||
}
|
||||
response.OkWithData(item, c)
|
||||
}
|
||||
|
||||
// UserExchange 用户发起兑换
|
||||
// @Tags 兑换中心-用户
|
||||
// @Summary 发起兑换
|
||||
// @Security BearerAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.ExchangeReq true "兑换请求"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"兑换成功"}"
|
||||
// @Router /exchange/redeem [post]
|
||||
func (a *ExchangeApi) UserExchange(c *gin.Context) {
|
||||
var req request.ExchangeReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.FailWithMsg("请求参数错误", c)
|
||||
return
|
||||
}
|
||||
userId := auth.GetUserId(c)
|
||||
if err := exchangeService.UserExchange(req, userId); err != nil {
|
||||
response.FailWithMsg(err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMsg("兑换成功", c)
|
||||
}
|
||||
|
||||
// UserOrderList 用户订单列表
|
||||
// @Tags 兑换中心-用户
|
||||
// @Summary 用户兑换记录
|
||||
// @Security BearerAuth
|
||||
// @Produce application/json
|
||||
// @Param current query int false "页码"
|
||||
// @Param pageSize query int false "每页大小"
|
||||
// @Param status query int false "订单状态"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"查询成功"}"
|
||||
// @Router /exchange/orders [get]
|
||||
func (a *ExchangeApi) UserOrderList(c *gin.Context) {
|
||||
var req request.ExchangeOrderListReq
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
response.FailWithMsg("请求参数错误", c)
|
||||
return
|
||||
}
|
||||
userId := auth.GetUserId(c)
|
||||
list, total, err := exchangeService.UserOrderList(req, userId)
|
||||
if err != nil {
|
||||
global.Logger.Error("获取订单列表失败", zap.Error(err))
|
||||
response.FailWithMsg("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithData(response.PageResult{
|
||||
List: list,
|
||||
Total: total,
|
||||
Page: req.Current,
|
||||
PageSize: req.PageSize,
|
||||
}, c)
|
||||
}
|
||||
+3
-1
@@ -44,7 +44,7 @@ func MigrateTable() {
|
||||
plant.CareTask{}, //植物养护任务
|
||||
plant.CareRecord{}, //植物养护记录
|
||||
plant.GrowthRecord{}, //植物成长记录
|
||||
plant.MediaCheckResult{}, //媒体安全检测结果
|
||||
//plant.MediaCheckResult{}, //媒体安全检测结果
|
||||
plant.Topic{}, //帖子话题
|
||||
plant.Post{}, //帖子
|
||||
plant.PostLike{}, //帖子点赞
|
||||
@@ -59,6 +59,8 @@ func MigrateTable() {
|
||||
plant.UserBadge{}, //用户徽章
|
||||
plant.UserStar{}, //用户收藏
|
||||
|
||||
plant.ExchangeItem{}, //兑换商品
|
||||
plant.ExchangeOrder{}, //兑换订单
|
||||
)
|
||||
if err != nil {
|
||||
global.Logger.Error("Migrate table failed,err:", zap.Error(err))
|
||||
|
||||
@@ -59,6 +59,7 @@ func Routers() {
|
||||
plantGroup.InitLevelConfigRouter(NeedAuthGroup) //等级配置
|
||||
plantGroup.InitBadgeConfigRouter(NeedAuthGroup) //徽章配置
|
||||
plantGroup.InitUserProfileRouter(NeedAuthGroup) //用户资料
|
||||
plantGroup.InitExchangeRouter(NeedAuthGroup) //兑换中心
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package plant
|
||||
|
||||
import (
|
||||
"sundynix-go/global"
|
||||
"sundynix-go/model/system"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ExchangeItem 兑换商品
|
||||
type ExchangeItem struct {
|
||||
global.BaseModel
|
||||
Name string `json:"name" gorm:"size:100;not null;column:name"` // 商品名称
|
||||
Description string `json:"description" gorm:"type:text;column:description"` // 商品描述
|
||||
ImageId string `json:"imageId" gorm:"size:50;column:image_id"` // 商品图片ID
|
||||
Type string `json:"type" gorm:"size:20;not null;default:PHYSICAL;column:type"` // 商品类型: PHYSICAL/VIRTUAL/COUPON
|
||||
CostSunlight int64 `json:"costSunlight" gorm:"not null;default:0;column:cost_sunlight"` // 消耗阳光值
|
||||
Stock int `json:"stock" gorm:"not null;default:-1;column:stock"` // 库存 -1无限
|
||||
LimitPerUser int `json:"limitPerUser" gorm:"not null;default:0;column:limit_per_user"` // 每人限兑次数 0不限
|
||||
Status int `json:"status" gorm:"not null;default:1;column:status"` // 1上架 2下架
|
||||
Sort int `json:"sort" gorm:"not null;default:0;column:sort"` // 排序
|
||||
StartTime *time.Time `json:"startTime" gorm:"column:start_time"` // 上架时间
|
||||
EndTime *time.Time `json:"endTime" gorm:"column:end_time"` // 下架时间
|
||||
Image *system.Oss `json:"image" gorm:"foreignKey:ImageId"` // 商品图片关联
|
||||
}
|
||||
|
||||
func (ExchangeItem) TableName() string {
|
||||
return "sundynix_exchange_item"
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package plant
|
||||
|
||||
import (
|
||||
"sundynix-go/global"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ExchangeOrder 兑换订单
|
||||
type ExchangeOrder struct {
|
||||
global.BaseModel
|
||||
UserId string `json:"userId" gorm:"index;size:50;not null;column:user_id"` // 用户ID
|
||||
ItemId string `json:"itemId" gorm:"index;size:50;not null;column:item_id"` // 商品ID
|
||||
ItemName string `json:"itemName" gorm:"size:100;column:item_name"` // 商品名称(冗余快照)
|
||||
CostSunlight int64 `json:"costSunlight" gorm:"not null;default:0;column:cost_sunlight"` // 消耗阳光值
|
||||
Quantity int `json:"quantity" gorm:"not null;default:1;column:quantity"` // 数量
|
||||
Status int `json:"status" gorm:"not null;default:1;column:status"` // 1待处理 2处理中 3已发货 4已完成 5已取消
|
||||
ItemType string `json:"itemType" gorm:"size:20;column:item_type"` // 商品类型快照
|
||||
RecipientName string `json:"recipientName" gorm:"size:50;column:recipient_name"` // 收货人姓名
|
||||
Phone string `json:"phone" gorm:"size:20;column:phone"` // 联系电话
|
||||
Address string `json:"address" gorm:"size:255;column:address"` // 收货地址
|
||||
TrackingNo string `json:"trackingNo" gorm:"size:100;column:tracking_no"` // 快递单号
|
||||
Remark string `json:"remark" gorm:"size:255;column:remark"` // 备注
|
||||
CompletedAt *time.Time `json:"completedAt" gorm:"column:completed_at"` // 完成时间
|
||||
Item *ExchangeItem `json:"item" gorm:"foreignKey:ItemId"` // 商品关联
|
||||
}
|
||||
|
||||
func (ExchangeOrder) TableName() string {
|
||||
return "sundynix_exchange_order"
|
||||
}
|
||||
|
||||
// 订单状态常量
|
||||
const (
|
||||
OrderStatusPending = 1 // 待处理
|
||||
OrderStatusProcessing = 2 // 处理中
|
||||
OrderStatusShipped = 3 // 已发货
|
||||
OrderStatusCompleted = 4 // 已完成
|
||||
OrderStatusCancelled = 5 // 已取消
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
package plant
|
||||
|
||||
import (
|
||||
"sundynix-go/global"
|
||||
)
|
||||
|
||||
// MediaCheckResult 媒体安全检测结果
|
||||
// 对应微信 media_check_async 接口的返回结果
|
||||
type MediaCheckResult struct {
|
||||
global.BaseModel
|
||||
TraceId string `json:"traceId" gorm:"column:trace_id;size:100;uniqueIndex;comment:微信返回的唯一任务id"`
|
||||
PostId string `json:"postId" gorm:"column:post_id;size:50;index;comment:关联的帖子id"`
|
||||
OssId string `json:"ossId" gorm:"column:oss_id;size:50;comment:关联的oss文件id"`
|
||||
UserId string `json:"userId" gorm:"column:user_id;size:50;comment:提交检测的用户id"`
|
||||
Status int `json:"status" gorm:"column:status;default:0;comment:检测状态 0:检测中 1:通过 2:违规"`
|
||||
Type int `json:"type" gorm:"column:type;comment:媒体类型 1:音频 2:图片"`
|
||||
ErrMsg string `json:"errMsg" gorm:"column:err_msg;size:255;comment:错误信息"`
|
||||
}
|
||||
|
||||
func (MediaCheckResult) TableName() string {
|
||||
return "sundynix_media_check_result"
|
||||
}
|
||||
@@ -2,9 +2,6 @@ package plant
|
||||
|
||||
import (
|
||||
"sundynix-go/global"
|
||||
"sundynix-go/utils/timer"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// CarePlan 养护计划
|
||||
@@ -19,28 +16,28 @@ type CarePlan struct {
|
||||
}
|
||||
|
||||
// AfterUpdate 钩子函数 修改计划后重新生成任务
|
||||
func (p *CarePlan) AfterUpdate(tx *gorm.DB) error {
|
||||
//1.删除旧任务
|
||||
err := tx.Where("plan_id = ?", p.Id).Unscoped().Delete(&CareTask{}).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
//2.创建新任务
|
||||
today := timer.GetZeroTime()
|
||||
dueDate := today.AddDate(0, 0, p.Period)
|
||||
task := CareTask{
|
||||
UserId: p.UserId,
|
||||
PlantId: p.Id,
|
||||
PlanId: p.Id,
|
||||
Name: p.Name,
|
||||
Icon: p.Icon,
|
||||
DueDate: dueDate,
|
||||
Status: 1,
|
||||
}
|
||||
err = tx.Create(&task).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
// func (p *CarePlan) AfterUpdate(tx *gorm.DB) error {
|
||||
// //1.删除旧任务
|
||||
// err := tx.Where("plan_id = ?", p.Id).Unscoped().Delete(&CareTask{}).Error
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// //2.创建新任务
|
||||
// today := timer.GetZeroTime()
|
||||
// dueDate := today.AddDate(0, 0, p.Period)
|
||||
// task := CareTask{
|
||||
// UserId: p.UserId,
|
||||
// PlantId: p.Id,
|
||||
// PlanId: p.Id,
|
||||
// Name: p.Name,
|
||||
// Icon: p.Icon,
|
||||
// DueDate: dueDate,
|
||||
// Status: 1,
|
||||
// }
|
||||
// err = tx.Create(&task).Error
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// return nil
|
||||
|
||||
}
|
||||
// }
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
package request
|
||||
|
||||
import common "sundynix-go/model/commom/request"
|
||||
|
||||
// ExchangeItemCreateReq 创建兑换商品请求
|
||||
type ExchangeItemCreateReq struct {
|
||||
Name string `json:"name" binding:"required"`
|
||||
Description string `json:"description"`
|
||||
ImageId string `json:"imageId"`
|
||||
Type string `json:"type" binding:"required"` // PHYSICAL / VIRTUAL / COUPON
|
||||
CostSunlight int64 `json:"costSunlight" binding:"required,min=1"`
|
||||
Stock int `json:"stock"` // -1 无限
|
||||
LimitPerUser int `json:"limitPerUser"` // 0 不限
|
||||
Sort int `json:"sort"`
|
||||
StartTime string `json:"startTime"` // 可选
|
||||
EndTime string `json:"endTime"` // 可选
|
||||
}
|
||||
|
||||
// ExchangeItemUpdateReq 更新兑换商品请求
|
||||
type ExchangeItemUpdateReq struct {
|
||||
Id string `json:"id" binding:"required"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
ImageId string `json:"imageId"`
|
||||
Type string `json:"type"`
|
||||
CostSunlight int64 `json:"costSunlight"`
|
||||
Stock int `json:"stock"`
|
||||
LimitPerUser int `json:"limitPerUser"`
|
||||
Status int `json:"status"` // 1上架 2下架
|
||||
Sort int `json:"sort"`
|
||||
StartTime string `json:"startTime"`
|
||||
EndTime string `json:"endTime"`
|
||||
}
|
||||
|
||||
// ExchangeItemListReq 商品列表查询请求
|
||||
type ExchangeItemListReq struct {
|
||||
common.PageInfo
|
||||
Type string `json:"type" form:"type"` // 按类型筛选
|
||||
Status int `json:"status" form:"status"` // 按状态筛选
|
||||
}
|
||||
|
||||
// ExchangeReq 用户兑换请求
|
||||
type ExchangeReq struct {
|
||||
ItemId string `json:"itemId" binding:"required"`
|
||||
Quantity int `json:"quantity"` // 默认1
|
||||
RecipientName string `json:"recipientName"`
|
||||
Phone string `json:"phone"`
|
||||
Address string `json:"address"`
|
||||
}
|
||||
|
||||
// ExchangeOrderListReq 订单列表查询请求
|
||||
type ExchangeOrderListReq struct {
|
||||
common.PageInfo
|
||||
Status int `json:"status" form:"status"` // 按状态筛选
|
||||
UserId string `json:"userId" form:"userId"` // 管理端按用户筛选
|
||||
}
|
||||
|
||||
// ExchangeOrderUpdateReq 更新订单状态请求 (管理端)
|
||||
type ExchangeOrderUpdateReq struct {
|
||||
Id string `json:"id" binding:"required"`
|
||||
Status int `json:"status" binding:"required"` // 目标状态
|
||||
TrackingNo string `json:"trackingNo"` // 快递单号(发货时)
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
+3
-3
@@ -12,13 +12,13 @@ type Wiki struct {
|
||||
Name string `json:"name" form:"name" gorm:"column:name;size:50;comment:名称"`
|
||||
LatinName string `json:"latinName" form:"latinName" gorm:"size:100;column:latin_name;comment:拉丁名"`
|
||||
Aliases string `json:"aliases" form:"aliases" gorm:"size:100;column:aliases;comment:别名(逗号分隔)"`
|
||||
DistributionArea string `json:"distributionArea" form:"distributionArea" gorm:"size:100;column:distribution_area;comment:分布区域"` //分布区域
|
||||
DistributionArea string `json:"distributionArea" form:"distributionArea" gorm:"type:text;;column:distribution_area;comment:分布区域"` //分布区域
|
||||
//科学分类
|
||||
Genus string `json:"genus" form:"genus" gorm:"size:20;column:genus;comment:科属"` // 属
|
||||
Difficulty int `json:"difficulty" form:"difficulty" gorm:"column:difficulty;comment:种植难度"` //种植难度 1-5级
|
||||
//形态特征
|
||||
LifeCycle string `json:"lifeCycle" form:"lifeCycle" gorm:"size:20;column:life_cycle;comment:生命周期"` // 生命周期 一年生 二年生 多年生等
|
||||
GrowthHabit string `json:"growthHabit" form:"growthHabit" gorm:"size:200;column:growth_habit;comment:成长习性"` // 生长习性
|
||||
LifeCycle string `json:"lifeCycle" form:"lifeCycle" gorm:"type:text;column:life_cycle;comment:生命周期"` // 生命周期 一年生 二年生 多年生等
|
||||
GrowthHabit string `json:"growthHabit" form:"growthHabit" gorm:"type:text;column:growth_habit;comment:成长习性"` // 生长习性
|
||||
ReproductionMethod string `json:"reproductionMethod" form:"reproductionMethod" gorm:"size:200;column:reproduction_method;comment:繁殖方法"` //繁殖方法
|
||||
PestsDiseases string `json:"pestsDiseases" form:"pestsDiseases" gorm:"size:200;column:pests_diseases;comment:病虫害"`
|
||||
//光照
|
||||
|
||||
Vendored
BIN
Binary file not shown.
@@ -0,0 +1,14 @@
|
||||
package plant
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type CallbackRouter struct{}
|
||||
|
||||
func (s *CallbackRouter) InitCallbackRouter(Router *gin.RouterGroup) {
|
||||
callbackRouter := Router.Group("callback")
|
||||
{
|
||||
callbackRouter.POST("mediaCheck", callbackApi.MediaCheckCallback) // 接收微信媒体检测回调
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ type RouterGroup struct {
|
||||
BadgeConfigRouter
|
||||
UserProfileRouter
|
||||
CallbackRouter
|
||||
ExchangeRouter
|
||||
}
|
||||
|
||||
// 初始化路由
|
||||
@@ -27,4 +28,5 @@ var (
|
||||
userProfileApi = v1.ApiGroupApp.PlantApiGroup.UserProfileApi
|
||||
badgeConfigApi = v1.ApiGroupApp.PlantApiGroup.BadgeConfigApi
|
||||
callbackApi = v1.ApiGroupApp.PlantApiGroup.CallbackApi
|
||||
exchangeApi = v1.ApiGroupApp.PlantApiGroup.ExchangeApi
|
||||
)
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package plant
|
||||
|
||||
import "github.com/gin-gonic/gin"
|
||||
|
||||
type ExchangeRouter struct{}
|
||||
|
||||
func (r *ExchangeRouter) InitExchangeRouter(Router *gin.RouterGroup) {
|
||||
// ========== 用户端路由 ==========
|
||||
userRouter := Router.Group("exchange")
|
||||
{
|
||||
userRouter.GET("list", exchangeApi.UserItemList) // 商品列表
|
||||
userRouter.GET("detail", exchangeApi.UserItemDetail) // 商品详情
|
||||
userRouter.POST("redeem", exchangeApi.UserExchange) // 发起兑换
|
||||
userRouter.GET("orders", exchangeApi.UserOrderList) // 我的兑换记录
|
||||
}
|
||||
|
||||
// ========== 管理端路由 ==========
|
||||
adminRouter := Router.Group("exchange")
|
||||
{
|
||||
adminRouter.POST("item/create", exchangeApi.CreateItem) // 创建商品
|
||||
adminRouter.POST("item/update", exchangeApi.UpdateItem) // 更新商品
|
||||
adminRouter.POST("item/delete", exchangeApi.DeleteItem) // 删除商品
|
||||
adminRouter.POST("item/list", exchangeApi.AdminItemList) // 管理端商品列表
|
||||
adminRouter.POST("order/list", exchangeApi.AdminOrderList) // 管理端订单列表
|
||||
adminRouter.POST("order/update", exchangeApi.UpdateOrderStatus) // 更新订单状态
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package plant
|
||||
|
||||
import (
|
||||
"sundynix-go/global"
|
||||
"sundynix-go/model/plant"
|
||||
plantres "sundynix-go/model/plant/response"
|
||||
|
||||
"go.uber.org/zap"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type CallbackService struct{}
|
||||
|
||||
var CallbackServiceApp = new(CallbackService)
|
||||
|
||||
// HandleMediaCheckCallback 处理媒体检测回调
|
||||
func (s *CallbackService) HandleMediaCheckCallback(cb plantres.WeChatCheckResultCallback) error {
|
||||
global.Logger.Info("收到微信媒体检测回调", zap.String("traceId", cb.TraceId), zap.String("suggest", cb.Result.Suggest))
|
||||
|
||||
var checkResult plant.MediaCheckResult
|
||||
err := global.DB.Where("trace_id = ?", cb.TraceId).First(&checkResult).Error
|
||||
if err != nil {
|
||||
global.Logger.Error("回调traceId未找到", zap.String("traceId", cb.TraceId), zap.Error(err))
|
||||
return err
|
||||
}
|
||||
|
||||
// 1. 更新检测结果状态
|
||||
status := 0
|
||||
if cb.Result.Suggest == "pass" {
|
||||
status = 1
|
||||
} else {
|
||||
status = 2
|
||||
}
|
||||
|
||||
err = global.DB.Model(&checkResult).Updates(map[string]interface{}{
|
||||
"status": status,
|
||||
"err_msg": cb.Result.Suggest,
|
||||
}).Error
|
||||
|
||||
if err != nil {
|
||||
global.Logger.Error("更新检测结果失败", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
|
||||
// 2. 根据结果处理帖子状态
|
||||
return s.updatePostStatus(checkResult.PostId)
|
||||
}
|
||||
|
||||
// updatePostStatus 更新帖子状态
|
||||
// 逻辑:
|
||||
// 1. 如果有任意一个检测结果为违规(2) -> 帖子违规(2)
|
||||
// 2. 如果所有检测结果都为通过(1) -> 帖子通过(1)
|
||||
// 3. 否则保持待审核(0)
|
||||
func (s *CallbackService) updatePostStatus(postId string) error {
|
||||
return global.DB.Transaction(func(tx *gorm.DB) error {
|
||||
var post plant.Post
|
||||
if err := tx.Where("id = ?", postId).First(&post).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 如果帖子已经是违规状态,无需再处理(可能之前已经由文本检测判定违规)
|
||||
if post.HasReviewed == 2 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var results []plant.MediaCheckResult
|
||||
if err := tx.Where("post_id = ?", postId).Find(&results).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hasRisky := false
|
||||
allPass := true
|
||||
|
||||
for _, res := range results {
|
||||
if res.Status == 2 {
|
||||
hasRisky = true
|
||||
break
|
||||
}
|
||||
if res.Status != 1 {
|
||||
allPass = false
|
||||
}
|
||||
}
|
||||
|
||||
var newStatus = post.HasReviewed // 默认保持原状态
|
||||
|
||||
if hasRisky {
|
||||
newStatus = 2
|
||||
// TODO: 这里可以执行额外的封禁逻辑,例如不仅标记违规,还软删除Oss关联等
|
||||
global.Logger.Warn("帖子包含违规图片,标记为违规", zap.String("postId", postId))
|
||||
} else if allPass {
|
||||
// 只有当所有图片都通过,且原状态不是违规时,才标记为通过
|
||||
// 注意:这里假设文本检测已经通过(文本检测是同步的,若不通过早已设为2)
|
||||
// 如果文本检测尚未完成(理论上不可能,因为是先文本后图片),这里可能会有竞态,但文本检测在发帖goroutine中是串行的。
|
||||
// 唯一需要注意的是,如果文本检测还在进行中,这里不应覆盖。
|
||||
// 但我们在PublishPost中是先改HasReviewed再发图片检查。
|
||||
// 如果文本通过,HasReviewed会被设为1? 不,根据新逻辑,PublishPost中只有无图才设为1。
|
||||
// 有图时,PublishPost中HasReviewed保持0。
|
||||
newStatus = 1
|
||||
global.Logger.Info("帖子所有图片检测通过,标记为通过", zap.String("postId", postId))
|
||||
}
|
||||
|
||||
if newStatus != post.HasReviewed {
|
||||
if err := tx.Model(&post).Update("has_reviewed", newStatus).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -11,4 +11,5 @@ type ServiceGroup struct {
|
||||
BadgeConfigService
|
||||
UserProfileService
|
||||
CallbackService
|
||||
ExchangeService
|
||||
}
|
||||
|
||||
@@ -0,0 +1,282 @@
|
||||
package plant
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sundynix-go/global"
|
||||
"sundynix-go/model/plant"
|
||||
plantReq "sundynix-go/model/plant/request"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type ExchangeService struct{}
|
||||
|
||||
// ==================== 管理端 ====================
|
||||
|
||||
// CreateItem 创建兑换商品
|
||||
func (s *ExchangeService) CreateItem(req plantReq.ExchangeItemCreateReq) error {
|
||||
item := plant.ExchangeItem{
|
||||
Name: req.Name,
|
||||
Description: req.Description,
|
||||
ImageId: req.ImageId,
|
||||
Type: req.Type,
|
||||
CostSunlight: req.CostSunlight,
|
||||
Stock: req.Stock,
|
||||
LimitPerUser: req.LimitPerUser,
|
||||
Sort: req.Sort,
|
||||
Status: 1, // 默认上架
|
||||
}
|
||||
if req.StartTime != "" {
|
||||
t, _ := time.Parse("2006-01-02 15:04:05", req.StartTime)
|
||||
item.StartTime = &t
|
||||
}
|
||||
if req.EndTime != "" {
|
||||
t, _ := time.Parse("2006-01-02 15:04:05", req.EndTime)
|
||||
item.EndTime = &t
|
||||
}
|
||||
return global.DB.Create(&item).Error
|
||||
}
|
||||
|
||||
// UpdateItem 更新兑换商品
|
||||
func (s *ExchangeService) UpdateItem(req plantReq.ExchangeItemUpdateReq) error {
|
||||
updateMap := map[string]interface{}{
|
||||
"name": req.Name,
|
||||
"description": req.Description,
|
||||
"image_id": req.ImageId,
|
||||
"type": req.Type,
|
||||
"cost_sunlight": req.CostSunlight,
|
||||
"stock": req.Stock,
|
||||
"limit_per_user": req.LimitPerUser,
|
||||
"status": req.Status,
|
||||
"sort": req.Sort,
|
||||
}
|
||||
if req.StartTime != "" {
|
||||
t, _ := time.Parse("2006-01-02 15:04:05", req.StartTime)
|
||||
updateMap["start_time"] = t
|
||||
}
|
||||
if req.EndTime != "" {
|
||||
t, _ := time.Parse("2006-01-02 15:04:05", req.EndTime)
|
||||
updateMap["end_time"] = t
|
||||
}
|
||||
return global.DB.Model(&plant.ExchangeItem{}).Where("id = ?", req.Id).Updates(updateMap).Error
|
||||
}
|
||||
|
||||
// DeleteItem 删除兑换商品
|
||||
func (s *ExchangeService) DeleteItem(id string) error {
|
||||
return global.DB.Where("id = ?", id).Delete(&plant.ExchangeItem{}).Error
|
||||
}
|
||||
|
||||
// AdminItemList 管理端商品列表(含下架)
|
||||
func (s *ExchangeService) AdminItemList(req plantReq.ExchangeItemListReq) (list []plant.ExchangeItem, total int64, err error) {
|
||||
db := global.DB.Model(&plant.ExchangeItem{}).Preload("Image")
|
||||
if req.Type != "" {
|
||||
db = db.Where("type = ?", req.Type)
|
||||
}
|
||||
if req.Status != 0 {
|
||||
db = db.Where("status = ?", req.Status)
|
||||
}
|
||||
if req.Keyword != "" {
|
||||
db = db.Where("name LIKE ?", "%"+req.Keyword+"%")
|
||||
}
|
||||
err = db.Count(&total).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = db.Scopes(req.Paginate()).Order("sort asc, created_at desc").Find(&list).Error
|
||||
return
|
||||
}
|
||||
|
||||
// AdminOrderList 管理端订单列表
|
||||
func (s *ExchangeService) AdminOrderList(req plantReq.ExchangeOrderListReq) (list []plant.ExchangeOrder, total int64, err error) {
|
||||
db := global.DB.Model(&plant.ExchangeOrder{}).Preload("Item", func(db *gorm.DB) *gorm.DB {
|
||||
return db.Preload("Image")
|
||||
})
|
||||
if req.Status != 0 {
|
||||
db = db.Where("status = ?", req.Status)
|
||||
}
|
||||
if req.UserId != "" {
|
||||
db = db.Where("user_id = ?", req.UserId)
|
||||
}
|
||||
if req.Keyword != "" {
|
||||
db = db.Where("item_name LIKE ? OR recipient_name LIKE ?", "%"+req.Keyword+"%", "%"+req.Keyword+"%")
|
||||
}
|
||||
err = db.Count(&total).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = db.Scopes(req.Paginate()).Order("created_at desc").Find(&list).Error
|
||||
return
|
||||
}
|
||||
|
||||
// UpdateOrderStatus 更新订单状态(管理端)
|
||||
func (s *ExchangeService) UpdateOrderStatus(req plantReq.ExchangeOrderUpdateReq) error {
|
||||
updateMap := map[string]interface{}{
|
||||
"status": req.Status,
|
||||
}
|
||||
if req.TrackingNo != "" {
|
||||
updateMap["tracking_no"] = req.TrackingNo
|
||||
}
|
||||
if req.Remark != "" {
|
||||
updateMap["remark"] = req.Remark
|
||||
}
|
||||
if req.Status == plant.OrderStatusCompleted {
|
||||
now := time.Now()
|
||||
updateMap["completed_at"] = now
|
||||
}
|
||||
// 如果取消订单,退还阳光值
|
||||
if req.Status == plant.OrderStatusCancelled {
|
||||
return global.DB.Transaction(func(tx *gorm.DB) error {
|
||||
var order plant.ExchangeOrder
|
||||
if err := tx.Where("id = ?", req.Id).First(&order).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if order.Status == plant.OrderStatusCancelled {
|
||||
return errors.New("订单已取消")
|
||||
}
|
||||
// 退还阳光值
|
||||
if err := tx.Model(&plant.UserProfile{}).Where("user_id = ?", order.UserId).
|
||||
Update("current_sunlight", gorm.Expr("current_sunlight + ?", order.CostSunlight)).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
// 恢复库存
|
||||
if err := tx.Model(&plant.ExchangeItem{}).Where("id = ? AND stock >= 0", order.ItemId).
|
||||
Update("stock", gorm.Expr("stock + ?", order.Quantity)).Error; err != nil {
|
||||
// 库存为-1(无限)时,WHERE条件不匹配,不影响
|
||||
}
|
||||
return tx.Model(&plant.ExchangeOrder{}).Where("id = ?", req.Id).Updates(updateMap).Error
|
||||
})
|
||||
}
|
||||
return global.DB.Model(&plant.ExchangeOrder{}).Where("id = ?", req.Id).Updates(updateMap).Error
|
||||
}
|
||||
|
||||
// ==================== 用户端 ====================
|
||||
|
||||
// UserItemList 用户端商品列表(仅上架)
|
||||
func (s *ExchangeService) UserItemList(req plantReq.ExchangeItemListReq) (list []plant.ExchangeItem, total int64, err error) {
|
||||
db := global.DB.Model(&plant.ExchangeItem{}).Preload("Image").
|
||||
Where("status = 1")
|
||||
if req.Type != "" {
|
||||
db = db.Where("type = ?", req.Type)
|
||||
}
|
||||
if req.Keyword != "" {
|
||||
db = db.Where("name LIKE ?", "%"+req.Keyword+"%")
|
||||
}
|
||||
err = db.Count(&total).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = db.Scopes(req.Paginate()).Order("sort asc, created_at desc").Find(&list).Error
|
||||
return
|
||||
}
|
||||
|
||||
// UserItemDetail 商品详情
|
||||
func (s *ExchangeService) UserItemDetail(itemId string) (plant.ExchangeItem, error) {
|
||||
var item plant.ExchangeItem
|
||||
err := global.DB.Preload("Image").Where("id = ? AND status = 1", itemId).First(&item).Error
|
||||
return item, err
|
||||
}
|
||||
|
||||
// UserExchange 用户发起兑换
|
||||
func (s *ExchangeService) UserExchange(req plantReq.ExchangeReq, userId string) error {
|
||||
if req.Quantity <= 0 {
|
||||
req.Quantity = 1
|
||||
}
|
||||
|
||||
return global.DB.Transaction(func(tx *gorm.DB) error {
|
||||
// 1. 查询并锁定商品
|
||||
var item plant.ExchangeItem
|
||||
if err := tx.Set("gorm:query_option", "FOR UPDATE").
|
||||
Where("id = ? AND status = 1", req.ItemId).First(&item).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return errors.New("商品不存在或已下架")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// 2. 检查有效期
|
||||
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("兑换已结束")
|
||||
}
|
||||
|
||||
// 3. 检查库存
|
||||
if item.Stock >= 0 && item.Stock < req.Quantity {
|
||||
return errors.New("库存不足")
|
||||
}
|
||||
|
||||
// 4. 检查每人限兑
|
||||
if item.LimitPerUser > 0 {
|
||||
var count int64
|
||||
tx.Model(&plant.ExchangeOrder{}).Where("user_id = ? AND item_id = ? AND status != ?",
|
||||
userId, req.ItemId, plant.OrderStatusCancelled).Count(&count)
|
||||
if int(count)+req.Quantity > item.LimitPerUser {
|
||||
return errors.New("已达到兑换上限")
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 计算总消耗
|
||||
totalCost := item.CostSunlight * int64(req.Quantity)
|
||||
|
||||
// 6. 扣减阳光值
|
||||
result := tx.Model(&plant.UserProfile{}).
|
||||
Where("user_id = ? AND current_sunlight >= ?", userId, totalCost).
|
||||
Update("current_sunlight", gorm.Expr("current_sunlight - ?", totalCost))
|
||||
if result.Error != nil {
|
||||
return result.Error
|
||||
}
|
||||
if result.RowsAffected == 0 {
|
||||
return errors.New("阳光值不足")
|
||||
}
|
||||
|
||||
// 7. 扣减库存
|
||||
if item.Stock >= 0 {
|
||||
if err := tx.Model(&plant.ExchangeItem{}).Where("id = ? AND stock >= ?", item.Id, req.Quantity).
|
||||
Update("stock", gorm.Expr("stock - ?", req.Quantity)).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// 8. 创建订单
|
||||
order := plant.ExchangeOrder{
|
||||
UserId: userId,
|
||||
ItemId: item.Id,
|
||||
ItemName: item.Name,
|
||||
CostSunlight: totalCost,
|
||||
Quantity: req.Quantity,
|
||||
Status: plant.OrderStatusPending,
|
||||
ItemType: item.Type,
|
||||
RecipientName: req.RecipientName,
|
||||
Phone: req.Phone,
|
||||
Address: req.Address,
|
||||
}
|
||||
|
||||
// 虚拟商品自动完成
|
||||
if item.Type == "VIRTUAL" {
|
||||
order.Status = plant.OrderStatusCompleted
|
||||
now := time.Now()
|
||||
order.CompletedAt = &now
|
||||
}
|
||||
|
||||
return tx.Create(&order).Error
|
||||
})
|
||||
}
|
||||
|
||||
// UserOrderList 用户订单列表
|
||||
func (s *ExchangeService) UserOrderList(req plantReq.ExchangeOrderListReq, userId string) (list []plant.ExchangeOrder, total int64, err error) {
|
||||
db := global.DB.Model(&plant.ExchangeOrder{}).Preload("Item", func(db *gorm.DB) *gorm.DB {
|
||||
return db.Preload("Image")
|
||||
}).Where("user_id = ?", userId)
|
||||
if req.Status != 0 {
|
||||
db = db.Where("status = ?", req.Status)
|
||||
}
|
||||
err = db.Count(&total).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = db.Scopes(req.Paginate()).Order("created_at desc").Find(&list).Error
|
||||
return
|
||||
}
|
||||
@@ -161,7 +161,7 @@ func (s *MyPlantService) UpdatePlant(req plantReq.UpdateMyPlant) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
//3.重新生成任务 CarePlans 结构体中使用钩子函数自动执行
|
||||
//3.重新生成任务
|
||||
//3.1 删除旧任务
|
||||
err = tx.Where("plan_id = ?", plan.Id).Unscoped().Delete(&plant.CareTask{}).Error
|
||||
if err != nil {
|
||||
@@ -177,6 +177,7 @@ func (s *MyPlantService) UpdatePlant(req plantReq.UpdateMyPlant) error {
|
||||
Icon: plan.Icon,
|
||||
DueDate: dueDate,
|
||||
Status: 1,
|
||||
TargetAction: plan.TargetAction,
|
||||
}
|
||||
err = tx.Create(&task).Error
|
||||
if err != nil {
|
||||
|
||||
@@ -173,7 +173,7 @@ func (s *WikiService) WikiPage(req plantReq.WikiPage, userId string) (list inter
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = db.Limit(limit).Offset(offset).Order("created_at desc").Find(&wikis).Error
|
||||
err = db.Limit(limit).Offset(offset).Order("is_hot desc,created_at desc").Find(&wikis).Error
|
||||
|
||||
// 优化 N+1 查询
|
||||
var wikiIds []string
|
||||
|
||||
Binary file not shown.
@@ -47,8 +47,11 @@ func SendCareMsg() error {
|
||||
// 将tasks分组,key为用户id 保证无论用户有多少植物,只给用户发送一条消息
|
||||
tasksMap := make(map[string][]*plant.CareTask)
|
||||
for _, task := range tasks {
|
||||
//用户id不为空再添加
|
||||
if task.UserId != "" {
|
||||
tasksMap[task.UserId] = append(tasksMap[task.UserId], task)
|
||||
}
|
||||
}
|
||||
for userId, cares := range tasksMap {
|
||||
//1.查询用户
|
||||
var user system.User
|
||||
@@ -78,8 +81,8 @@ func SendCareMsg() error {
|
||||
Value: time.Date(now.Year(), now.Month(), now.Day(), 8, 30, 0, 0, time.Local).Format("2006-01-02"),
|
||||
},
|
||||
},
|
||||
//MiniProgramState: "formal",
|
||||
MiniProgramState: "trial",
|
||||
MiniProgramState: "formal",
|
||||
//MiniProgramState: "trial",
|
||||
Lang: "zh_CN",
|
||||
}
|
||||
payloadBytes, err := json.Marshal(payload)
|
||||
|
||||
Reference in New Issue
Block a user