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
|
||
} |