Files
2025-11-17 13:32:54 +08:00

350 lines
8.5 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

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

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