350 lines
8.5 KiB
Go
350 lines
8.5 KiB
Go
|
|
package service
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"dianshang/internal/model"
|
|||
|
|
"dianshang/internal/repository"
|
|||
|
|
"errors"
|
|||
|
|
"fmt"
|
|||
|
|
"time"
|
|||
|
|
"gorm.io/gorm"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// PointsService 积分服务
|
|||
|
|
type PointsService struct {
|
|||
|
|
pointsRepo *repository.PointsRepository
|
|||
|
|
db *gorm.DB
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// NewPointsService 创建积分服务
|
|||
|
|
func NewPointsService(pointsRepo *repository.PointsRepository, db *gorm.DB) *PointsService {
|
|||
|
|
return &PointsService{
|
|||
|
|
pointsRepo: pointsRepo,
|
|||
|
|
db: db,
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// GetUserPoints 获取用户积分
|
|||
|
|
func (s *PointsService) GetUserPoints(userID uint) (int, error) {
|
|||
|
|
return s.pointsRepo.GetUserPoints(userID)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// AddPoints 增加用户积分
|
|||
|
|
func (s *PointsService) AddPoints(userID uint, amount int, description string, orderID *uint, orderNo, productName string) error {
|
|||
|
|
if amount <= 0 {
|
|||
|
|
return errors.New("积分数量必须大于0")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 开启事务
|
|||
|
|
tx := s.db.Begin()
|
|||
|
|
defer func() {
|
|||
|
|
if r := recover(); r != nil {
|
|||
|
|
tx.Rollback()
|
|||
|
|
}
|
|||
|
|
}()
|
|||
|
|
|
|||
|
|
// 获取当前积分
|
|||
|
|
currentPoints, err := s.pointsRepo.GetUserPoints(userID)
|
|||
|
|
if err != nil {
|
|||
|
|
tx.Rollback()
|
|||
|
|
return err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 更新用户积分
|
|||
|
|
newPoints := currentPoints + amount
|
|||
|
|
err = s.pointsRepo.UpdateUserPoints(userID, newPoints)
|
|||
|
|
if err != nil {
|
|||
|
|
tx.Rollback()
|
|||
|
|
return err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 创建积分历史记录
|
|||
|
|
history := &model.PointsHistory{
|
|||
|
|
UserID: userID,
|
|||
|
|
Type: 1, // 获得
|
|||
|
|
Points: amount,
|
|||
|
|
Description: description,
|
|||
|
|
OrderID: orderID,
|
|||
|
|
OrderNo: orderNo,
|
|||
|
|
ProductName: productName,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
err = s.pointsRepo.CreatePointsHistory(history)
|
|||
|
|
if err != nil {
|
|||
|
|
tx.Rollback()
|
|||
|
|
return err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return tx.Commit().Error
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// DeductPoints 扣减用户积分
|
|||
|
|
func (s *PointsService) DeductPoints(userID uint, amount int, description string, orderID *uint, orderNo, productName string) error {
|
|||
|
|
if amount <= 0 {
|
|||
|
|
return errors.New("积分数量必须大于0")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 开启事务
|
|||
|
|
tx := s.db.Begin()
|
|||
|
|
defer func() {
|
|||
|
|
if r := recover(); r != nil {
|
|||
|
|
tx.Rollback()
|
|||
|
|
}
|
|||
|
|
}()
|
|||
|
|
|
|||
|
|
// 获取当前积分
|
|||
|
|
currentPoints, err := s.pointsRepo.GetUserPoints(userID)
|
|||
|
|
if err != nil {
|
|||
|
|
tx.Rollback()
|
|||
|
|
return err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 检查积分是否足够
|
|||
|
|
if currentPoints < amount {
|
|||
|
|
tx.Rollback()
|
|||
|
|
return errors.New("积分不足")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 更新用户积分
|
|||
|
|
newPoints := currentPoints - amount
|
|||
|
|
err = s.pointsRepo.UpdateUserPoints(userID, newPoints)
|
|||
|
|
if err != nil {
|
|||
|
|
tx.Rollback()
|
|||
|
|
return err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 创建积分历史记录
|
|||
|
|
history := &model.PointsHistory{
|
|||
|
|
UserID: userID,
|
|||
|
|
Type: 2, // 消费
|
|||
|
|
Points: amount,
|
|||
|
|
Description: description,
|
|||
|
|
OrderID: orderID,
|
|||
|
|
OrderNo: orderNo,
|
|||
|
|
ProductName: productName,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
err = s.pointsRepo.CreatePointsHistory(history)
|
|||
|
|
if err != nil {
|
|||
|
|
tx.Rollback()
|
|||
|
|
return err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return tx.Commit().Error
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// GetPointsHistory 获取积分历史记录
|
|||
|
|
func (s *PointsService) GetPointsHistory(userID uint, page, pageSize int) ([]model.PointsHistory, map[string]interface{}, error) {
|
|||
|
|
if page <= 0 {
|
|||
|
|
page = 1
|
|||
|
|
}
|
|||
|
|
if pageSize <= 0 || pageSize > 100 {
|
|||
|
|
pageSize = 20
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
histories, total, err := s.pointsRepo.GetPointsHistory(userID, page, pageSize)
|
|||
|
|
if err != nil {
|
|||
|
|
return nil, nil, err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 构建分页信息
|
|||
|
|
totalPages := (int(total) + pageSize - 1) / pageSize
|
|||
|
|
pagination := map[string]interface{}{
|
|||
|
|
"total": total,
|
|||
|
|
"page": page,
|
|||
|
|
"page_size": pageSize,
|
|||
|
|
"total_pages": totalPages,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return histories, pagination, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// GetPointsRules 获取积分规则列表
|
|||
|
|
func (s *PointsService) GetPointsRules() ([]model.PointsRule, error) {
|
|||
|
|
return s.pointsRepo.GetPointsRules()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// GetPointsExchangeList 获取积分兑换商品列表
|
|||
|
|
func (s *PointsService) GetPointsExchangeList() ([]model.PointsExchange, error) {
|
|||
|
|
return s.pointsRepo.GetPointsExchangeList()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ExchangePoints 积分兑换
|
|||
|
|
func (s *PointsService) ExchangePoints(userID, exchangeID uint) error {
|
|||
|
|
// 开启事务
|
|||
|
|
tx := s.db.Begin()
|
|||
|
|
defer func() {
|
|||
|
|
if r := recover(); r != nil {
|
|||
|
|
tx.Rollback()
|
|||
|
|
}
|
|||
|
|
}()
|
|||
|
|
|
|||
|
|
// 获取兑换商品信息
|
|||
|
|
exchange, err := s.pointsRepo.GetPointsExchangeByID(exchangeID)
|
|||
|
|
if err != nil {
|
|||
|
|
tx.Rollback()
|
|||
|
|
return errors.New("兑换商品不存在")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 检查库存
|
|||
|
|
if exchange.Stock > 0 && exchange.ExchangeCount >= exchange.Stock {
|
|||
|
|
tx.Rollback()
|
|||
|
|
return errors.New("商品库存不足")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取用户当前积分
|
|||
|
|
currentPoints, err := s.pointsRepo.GetUserPoints(userID)
|
|||
|
|
if err != nil {
|
|||
|
|
tx.Rollback()
|
|||
|
|
return err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 检查积分是否足够
|
|||
|
|
if currentPoints < exchange.Points {
|
|||
|
|
tx.Rollback()
|
|||
|
|
return errors.New("积分不足")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 扣减积分
|
|||
|
|
description := fmt.Sprintf("兑换商品:%s", exchange.Name)
|
|||
|
|
err = s.DeductPoints(userID, exchange.Points, description, nil, "", exchange.Name)
|
|||
|
|
if err != nil {
|
|||
|
|
tx.Rollback()
|
|||
|
|
return err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 创建兑换记录
|
|||
|
|
record := &model.PointsExchangeRecord{
|
|||
|
|
UserID: userID,
|
|||
|
|
PointsExchangeID: exchangeID,
|
|||
|
|
Points: exchange.Points,
|
|||
|
|
Status: 1, // 已兑换
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
err = s.pointsRepo.CreatePointsExchangeRecord(record)
|
|||
|
|
if err != nil {
|
|||
|
|
tx.Rollback()
|
|||
|
|
return err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 更新兑换次数
|
|||
|
|
err = s.pointsRepo.UpdatePointsExchangeCount(exchangeID)
|
|||
|
|
if err != nil {
|
|||
|
|
tx.Rollback()
|
|||
|
|
return err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return tx.Commit().Error
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// GetUserExchangeRecords 获取用户兑换记录
|
|||
|
|
func (s *PointsService) GetUserExchangeRecords(userID uint, page, pageSize int) ([]model.PointsExchangeRecord, map[string]interface{}, error) {
|
|||
|
|
if page <= 0 {
|
|||
|
|
page = 1
|
|||
|
|
}
|
|||
|
|
if pageSize <= 0 || pageSize > 100 {
|
|||
|
|
pageSize = 20
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
records, total, err := s.pointsRepo.GetUserExchangeRecords(userID, page, pageSize)
|
|||
|
|
if err != nil {
|
|||
|
|
return nil, nil, err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 构建分页信息
|
|||
|
|
totalPages := (int(total) + pageSize - 1) / pageSize
|
|||
|
|
pagination := map[string]interface{}{
|
|||
|
|
"total": total,
|
|||
|
|
"page": page,
|
|||
|
|
"page_size": pageSize,
|
|||
|
|
"total_pages": totalPages,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return records, pagination, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// GetPointsOverview 获取积分概览
|
|||
|
|
func (s *PointsService) GetPointsOverview(userID uint) (map[string]interface{}, error) {
|
|||
|
|
// 获取用户当前积分
|
|||
|
|
currentPoints, err := s.pointsRepo.GetUserPoints(userID)
|
|||
|
|
if err != nil {
|
|||
|
|
return nil, err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取积分历史统计
|
|||
|
|
var totalEarned, totalSpent, thisMonthEarned, thisMonthSpent int64
|
|||
|
|
|
|||
|
|
// 统计总获得积分
|
|||
|
|
s.db.Model(&model.PointsHistory{}).
|
|||
|
|
Where("user_id = ? AND type = ?", userID, 1).
|
|||
|
|
Select("COALESCE(SUM(points), 0)").
|
|||
|
|
Scan(&totalEarned)
|
|||
|
|
|
|||
|
|
// 统计总消费积分
|
|||
|
|
s.db.Model(&model.PointsHistory{}).
|
|||
|
|
Where("user_id = ? AND type = ?", userID, 2).
|
|||
|
|
Select("COALESCE(SUM(points), 0)").
|
|||
|
|
Scan(&totalSpent)
|
|||
|
|
|
|||
|
|
// 获取本月的开始时间和结束时间
|
|||
|
|
now := time.Now()
|
|||
|
|
firstDayOfMonth := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location())
|
|||
|
|
firstDayOfNextMonth := firstDayOfMonth.AddDate(0, 1, 0)
|
|||
|
|
|
|||
|
|
// 统计本月获得积分
|
|||
|
|
s.db.Model(&model.PointsHistory{}).
|
|||
|
|
Where("user_id = ? AND type = ? AND created_at >= ? AND created_at < ?",
|
|||
|
|
userID, 1, firstDayOfMonth, firstDayOfNextMonth).
|
|||
|
|
Select("COALESCE(SUM(points), 0)").
|
|||
|
|
Scan(&thisMonthEarned)
|
|||
|
|
|
|||
|
|
// 统计本月消费积分
|
|||
|
|
s.db.Model(&model.PointsHistory{}).
|
|||
|
|
Where("user_id = ? AND type = ? AND created_at >= ? AND created_at < ?",
|
|||
|
|
userID, 2, firstDayOfMonth, firstDayOfNextMonth).
|
|||
|
|
Select("COALESCE(SUM(points), 0)").
|
|||
|
|
Scan(&thisMonthSpent)
|
|||
|
|
|
|||
|
|
overview := map[string]interface{}{
|
|||
|
|
"total_points": currentPoints,
|
|||
|
|
"available_points": currentPoints,
|
|||
|
|
"frozen_points": 0,
|
|||
|
|
"total_earned": totalEarned,
|
|||
|
|
"total_spent": totalSpent,
|
|||
|
|
"this_month_earned": thisMonthEarned,
|
|||
|
|
"this_month_spent": thisMonthSpent,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return overview, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// CheckAndGiveDailyLoginPoints 检查并给予每日首次登录积分
|
|||
|
|
func (s *PointsService) CheckAndGiveDailyLoginPoints(userID uint) (bool, error) {
|
|||
|
|
// 获取今天的开始时间(00:00:00)
|
|||
|
|
now := time.Now()
|
|||
|
|
today := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
|
|||
|
|
tomorrow := today.Add(24 * time.Hour)
|
|||
|
|
|
|||
|
|
// 检查今天是否已经有登录积分记录
|
|||
|
|
var count int64
|
|||
|
|
err := s.db.Model(&model.PointsHistory{}).
|
|||
|
|
Where("user_id = ? AND description = ? AND created_at >= ? AND created_at < ?",
|
|||
|
|
userID, "每日首次登录", today, tomorrow).
|
|||
|
|
Count(&count).Error
|
|||
|
|
|
|||
|
|
if err != nil {
|
|||
|
|
return false, fmt.Errorf("检查每日登录记录失败: %v", err)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 如果今天已经有登录积分记录,则不再给予
|
|||
|
|
if count > 0 {
|
|||
|
|
return false, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 给予每日登录积分(1积分)
|
|||
|
|
err = s.AddPoints(userID, 1, "每日首次登录", nil, "", "")
|
|||
|
|
if err != nil {
|
|||
|
|
return false, fmt.Errorf("给予每日登录积分失败: %v", err)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return true, nil
|
|||
|
|
}
|