refactor: 统一权益系统命名 VIP -> Equity

后端更改:
- 重命名数据库表: vip_levels -> equity_levels, vip_level_rewards -> equity_level_rewards, user_vip_rewards -> user_equity_rewards
- 重命名实体类: VipLevel -> EquityLevel, VipLevelReward -> EquityLevelReward, UserVipReward -> UserEquityReward
- 重命名 DTO: VipInfoResponse -> EquityInfoResponse, VipUserInfoDto -> EquityUserInfoDto, VipLevelDto -> EquityLevelDto
- 重命名服务: VipService -> EquityService, IVipService -> IEquityService
- 更新 API 端点: /vip_list -> /equity_list
- 移动命名空间: Models/Vip -> Models/Equity
- 删除重复的 VipController (与 QyLevelController 功能重复)
- 删除未使用的 equity_levels 和 equity_level_prizes 旧表实体

前端更改:
- 更新 API 调用: getVipList -> getEquityList
- 更新 vip.vue 页面使用新的 API 函数

保持兼容:
- JSON 响应字段名保持不变 (vip, last_vip, jin_du 等)
- 用户表 vip 字段保持不变
This commit is contained in:
zpc 2026-02-05 18:21:07 +08:00
parent 799fb29e0e
commit 2550f6d4c2
28 changed files with 279 additions and 1104 deletions

View File

@ -40,11 +40,11 @@ export const updateUserInfo = async (data) => {
};
/**
* 获取VIP等级列表
* @returns {Promise} VIP列表
* 获取权益等级列表
* @returns {Promise} 权益等级列表
*/
export const getVipList = async () => {
return await RequestManager.get('/vip_list', {}, true);
export const getEquityList = async () => {
return await RequestManager.get('/equity_list', {}, true);
};
/**

View File

@ -42,7 +42,7 @@
</template>
<script>
import { getVipList } from '@/common/server/user.js';
import { getEquityList } from '@/common/server/user.js';
export default {
data() {
@ -61,7 +61,7 @@
methods: {
async getData() {
//
const res = await getVipList();
const res = await getEquityList();
if (res.status == 1) {
this.userinfo = res.data.userinfo
this.config = res.data.data

View File

@ -1,233 +0,0 @@
-- =============================================
-- 权益等级表创建脚本
-- 用于福利与任务管理模块
-- =============================================
-- 创建权益等级表
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[equity_levels]') AND type in (N'U'))
BEGIN
CREATE TABLE [dbo].[equity_levels] (
[id] INT IDENTITY(1,1) NOT NULL,
[level] INT NOT NULL,
[title] NVARCHAR(100) NOT NULL,
[required_points] INT NOT NULL DEFAULT 0,
[created_at] DATETIME2 DEFAULT GETDATE(),
[updated_at] DATETIME2 DEFAULT GETDATE(),
[deleted_at] DATETIME2 NULL,
CONSTRAINT [pk_equity_levels] PRIMARY KEY CLUSTERED ([id] ASC)
);
-- 添加表注释
EXEC sp_addextendedproperty
@name = N'MS_Description',
@value = N'权益等级表,存储用户权益等级配置',
@level0type = N'SCHEMA', @level0name = N'dbo',
@level1type = N'TABLE', @level1name = N'equity_levels';
-- 添加列注释
EXEC sp_addextendedproperty @name = N'MS_Description', @value = N'主键ID',
@level0type = N'SCHEMA', @level0name = N'dbo',
@level1type = N'TABLE', @level1name = N'equity_levels',
@level2type = N'COLUMN', @level2name = N'id';
EXEC sp_addextendedproperty @name = N'MS_Description', @value = N'等级',
@level0type = N'SCHEMA', @level0name = N'dbo',
@level1type = N'TABLE', @level1name = N'equity_levels',
@level2type = N'COLUMN', @level2name = N'level';
EXEC sp_addextendedproperty @name = N'MS_Description', @value = N'等级名称',
@level0type = N'SCHEMA', @level0name = N'dbo',
@level1type = N'TABLE', @level1name = N'equity_levels',
@level2type = N'COLUMN', @level2name = N'title';
EXEC sp_addextendedproperty @name = N'MS_Description', @value = N'所需欧气值',
@level0type = N'SCHEMA', @level0name = N'dbo',
@level1type = N'TABLE', @level1name = N'equity_levels',
@level2type = N'COLUMN', @level2name = N'required_points';
EXEC sp_addextendedproperty @name = N'MS_Description', @value = N'创建时间',
@level0type = N'SCHEMA', @level0name = N'dbo',
@level1type = N'TABLE', @level1name = N'equity_levels',
@level2type = N'COLUMN', @level2name = N'created_at';
EXEC sp_addextendedproperty @name = N'MS_Description', @value = N'更新时间',
@level0type = N'SCHEMA', @level0name = N'dbo',
@level1type = N'TABLE', @level1name = N'equity_levels',
@level2type = N'COLUMN', @level2name = N'updated_at';
EXEC sp_addextendedproperty @name = N'MS_Description', @value = N'删除时间(软删除)',
@level0type = N'SCHEMA', @level0name = N'dbo',
@level1type = N'TABLE', @level1name = N'equity_levels',
@level2type = N'COLUMN', @level2name = N'deleted_at';
-- 创建索引
CREATE INDEX [ix_equity_levels_level] ON [dbo].[equity_levels] ([level]);
CREATE INDEX [ix_equity_levels_deleted_at] ON [dbo].[equity_levels] ([deleted_at]);
PRINT '权益等级表 equity_levels 创建成功';
END
ELSE
BEGIN
PRINT '权益等级表 equity_levels 已存在';
END
GO
-- 创建权益等级奖品表
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[equity_level_prizes]') AND type in (N'U'))
BEGIN
CREATE TABLE [dbo].[equity_level_prizes] (
[id] INT IDENTITY(1,1) NOT NULL,
[qy_level_id] INT NOT NULL,
[qy_level] INT NOT NULL,
[type] TINYINT NOT NULL DEFAULT 2,
[title] NVARCHAR(255) NULL,
[coupon_id] INT NULL,
[z_num] INT NULL DEFAULT 0,
[man_price] DECIMAL(10,2) NULL DEFAULT 0,
[jian_price] DECIMAL(10,2) NULL DEFAULT 0,
[effective_day] INT NULL DEFAULT 0,
[jiang_price] DECIMAL(10,2) NULL DEFAULT 0,
[money] DECIMAL(10,2) NULL DEFAULT 0,
[sc_money] DECIMAL(10,2) NULL DEFAULT 0,
[probability] DECIMAL(5,2) NULL DEFAULT 0,
[imgurl] NVARCHAR(500) NULL,
[prize_code] NVARCHAR(100) NULL,
[shang_id] INT NULL,
[sort] INT NULL DEFAULT 0,
[created_at] DATETIME2 DEFAULT GETDATE(),
[updated_at] DATETIME2 DEFAULT GETDATE(),
[deleted_at] DATETIME2 NULL,
CONSTRAINT [pk_equity_level_prizes] PRIMARY KEY CLUSTERED ([id] ASC),
CONSTRAINT [fk_equity_level_prizes_equity_levels] FOREIGN KEY ([qy_level_id])
REFERENCES [dbo].[equity_levels] ([id]) ON DELETE CASCADE
);
-- 添加表注释
EXEC sp_addextendedproperty
@name = N'MS_Description',
@value = N'权益等级奖品表,存储各等级对应的奖品配置',
@level0type = N'SCHEMA', @level0name = N'dbo',
@level1type = N'TABLE', @level1name = N'equity_level_prizes';
-- 添加列注释
EXEC sp_addextendedproperty @name = N'MS_Description', @value = N'主键ID',
@level0type = N'SCHEMA', @level0name = N'dbo',
@level1type = N'TABLE', @level1name = N'equity_level_prizes',
@level2type = N'COLUMN', @level2name = N'id';
EXEC sp_addextendedproperty @name = N'MS_Description', @value = N'权益等级ID',
@level0type = N'SCHEMA', @level0name = N'dbo',
@level1type = N'TABLE', @level1name = N'equity_level_prizes',
@level2type = N'COLUMN', @level2name = N'qy_level_id';
EXEC sp_addextendedproperty @name = N'MS_Description', @value = N'权益等级',
@level0type = N'SCHEMA', @level0name = N'dbo',
@level1type = N'TABLE', @level1name = N'equity_level_prizes',
@level2type = N'COLUMN', @level2name = N'qy_level';
EXEC sp_addextendedproperty @name = N'MS_Description', @value = N'奖品类型1-优惠券 2-实物奖品',
@level0type = N'SCHEMA', @level0name = N'dbo',
@level1type = N'TABLE', @level1name = N'equity_level_prizes',
@level2type = N'COLUMN', @level2name = N'type';
EXEC sp_addextendedproperty @name = N'MS_Description', @value = N'奖品标题',
@level0type = N'SCHEMA', @level0name = N'dbo',
@level1type = N'TABLE', @level1name = N'equity_level_prizes',
@level2type = N'COLUMN', @level2name = N'title';
EXEC sp_addextendedproperty @name = N'MS_Description', @value = N'优惠券ID',
@level0type = N'SCHEMA', @level0name = N'dbo',
@level1type = N'TABLE', @level1name = N'equity_level_prizes',
@level2type = N'COLUMN', @level2name = N'coupon_id';
EXEC sp_addextendedproperty @name = N'MS_Description', @value = N'优惠券数量',
@level0type = N'SCHEMA', @level0name = N'dbo',
@level1type = N'TABLE', @level1name = N'equity_level_prizes',
@level2type = N'COLUMN', @level2name = N'z_num';
EXEC sp_addextendedproperty @name = N'MS_Description', @value = N'满减门槛',
@level0type = N'SCHEMA', @level0name = N'dbo',
@level1type = N'TABLE', @level1name = N'equity_level_prizes',
@level2type = N'COLUMN', @level2name = N'man_price';
EXEC sp_addextendedproperty @name = N'MS_Description', @value = N'减免金额',
@level0type = N'SCHEMA', @level0name = N'dbo',
@level1type = N'TABLE', @level1name = N'equity_level_prizes',
@level2type = N'COLUMN', @level2name = N'jian_price';
EXEC sp_addextendedproperty @name = N'MS_Description', @value = N'有效天数',
@level0type = N'SCHEMA', @level0name = N'dbo',
@level1type = N'TABLE', @level1name = N'equity_level_prizes',
@level2type = N'COLUMN', @level2name = N'effective_day';
EXEC sp_addextendedproperty @name = N'MS_Description', @value = N'奖品价值',
@level0type = N'SCHEMA', @level0name = N'dbo',
@level1type = N'TABLE', @level1name = N'equity_level_prizes',
@level2type = N'COLUMN', @level2name = N'jiang_price';
EXEC sp_addextendedproperty @name = N'MS_Description', @value = N'兑换价格',
@level0type = N'SCHEMA', @level0name = N'dbo',
@level1type = N'TABLE', @level1name = N'equity_level_prizes',
@level2type = N'COLUMN', @level2name = N'money';
EXEC sp_addextendedproperty @name = N'MS_Description', @value = N'市场参考价',
@level0type = N'SCHEMA', @level0name = N'dbo',
@level1type = N'TABLE', @level1name = N'equity_level_prizes',
@level2type = N'COLUMN', @level2name = N'sc_money';
EXEC sp_addextendedproperty @name = N'MS_Description', @value = N'中奖概率0-100',
@level0type = N'SCHEMA', @level0name = N'dbo',
@level1type = N'TABLE', @level1name = N'equity_level_prizes',
@level2type = N'COLUMN', @level2name = N'probability';
EXEC sp_addextendedproperty @name = N'MS_Description', @value = N'奖品图片URL',
@level0type = N'SCHEMA', @level0name = N'dbo',
@level1type = N'TABLE', @level1name = N'equity_level_prizes',
@level2type = N'COLUMN', @level2name = N'imgurl';
EXEC sp_addextendedproperty @name = N'MS_Description', @value = N'奖品编码',
@level0type = N'SCHEMA', @level0name = N'dbo',
@level1type = N'TABLE', @level1name = N'equity_level_prizes',
@level2type = N'COLUMN', @level2name = N'prize_code';
EXEC sp_addextendedproperty @name = N'MS_Description', @value = N'奖品等级ID',
@level0type = N'SCHEMA', @level0name = N'dbo',
@level1type = N'TABLE', @level1name = N'equity_level_prizes',
@level2type = N'COLUMN', @level2name = N'shang_id';
EXEC sp_addextendedproperty @name = N'MS_Description', @value = N'排序',
@level0type = N'SCHEMA', @level0name = N'dbo',
@level1type = N'TABLE', @level1name = N'equity_level_prizes',
@level2type = N'COLUMN', @level2name = N'sort';
EXEC sp_addextendedproperty @name = N'MS_Description', @value = N'创建时间',
@level0type = N'SCHEMA', @level0name = N'dbo',
@level1type = N'TABLE', @level1name = N'equity_level_prizes',
@level2type = N'COLUMN', @level2name = N'created_at';
EXEC sp_addextendedproperty @name = N'MS_Description', @value = N'更新时间',
@level0type = N'SCHEMA', @level0name = N'dbo',
@level1type = N'TABLE', @level1name = N'equity_level_prizes',
@level2type = N'COLUMN', @level2name = N'updated_at';
EXEC sp_addextendedproperty @name = N'MS_Description', @value = N'删除时间(软删除)',
@level0type = N'SCHEMA', @level0name = N'dbo',
@level1type = N'TABLE', @level1name = N'equity_level_prizes',
@level2type = N'COLUMN', @level2name = N'deleted_at';
-- 创建索引
CREATE INDEX [ix_equity_level_prizes_qy_level_id] ON [dbo].[equity_level_prizes] ([qy_level_id]);
CREATE INDEX [ix_equity_level_prizes_type] ON [dbo].[equity_level_prizes] ([type]);
CREATE INDEX [ix_equity_level_prizes_deleted_at] ON [dbo].[equity_level_prizes] ([deleted_at]);
CREATE INDEX [ix_equity_level_prizes_sort] ON [dbo].[equity_level_prizes] ([sort]);
PRINT '权益等级奖品表 equity_level_prizes 创建成功';
END
ELSE
BEGIN
PRINT '权益等级奖品表 equity_level_prizes 已存在';
END
GO
PRINT '权益等级表创建脚本执行完成';
GO

View File

@ -1,74 +0,0 @@
using HoneyBox.Admin.Business.Attributes;
using HoneyBox.Admin.Business.Models;
using HoneyBox.Admin.Business.Models.User;
using HoneyBox.Admin.Business.Services.Interfaces;
using Microsoft.AspNetCore.Mvc;
namespace HoneyBox.Admin.Business.Controllers;
/// <summary>
/// VIP等级管理控制器
/// </summary>
[Route("api/admin/business/vip")]
public class VipController : BusinessControllerBase
{
private readonly IUserBusinessService _userService;
public VipController(IUserBusinessService userService)
{
_userService = userService;
}
/// <summary>
/// 获取VIP等级列表
/// </summary>
/// <returns>VIP等级列表</returns>
[HttpGet]
[BusinessPermission("vip:list")]
public async Task<IActionResult> GetVipLevels()
{
try
{
var result = await _userService.GetVipLevelsAsync();
return Ok(result);
}
catch (BusinessException ex)
{
return Error(ex.Code, ex.Message);
}
}
/// <summary>
/// 更新VIP等级
/// </summary>
/// <param name="id">VIP等级ID</param>
/// <param name="request">更新请求</param>
/// <returns>操作结果</returns>
[HttpPut("{id:int}")]
[BusinessPermission("vip:edit")]
public async Task<IActionResult> UpdateVipLevel(int id, [FromBody] VipLevelUpdateRequest request)
{
if (!ModelState.IsValid)
{
var errors = ModelState.Values
.SelectMany(v => v.Errors)
.Select(e => e.ErrorMessage)
.FirstOrDefault();
return ValidationError(errors ?? "参数验证失败");
}
try
{
var result = await _userService.UpdateVipLevelAsync(id, request);
if (result)
{
return Ok("VIP等级更新成功");
}
return Error(BusinessErrorCodes.InternalError, "更新失败");
}
catch (BusinessException ex)
{
return Error(ex.Code, ex.Message);
}
}
}

View File

@ -92,9 +92,9 @@ public class UserListResponse
public decimal Diamond { get; set; }
/// <summary>
/// VIP等级
/// 权益等级
/// </summary>
public int VipLevel { get; set; }
public int EquityLevel { get; set; }
/// <summary>
/// 注册时间

View File

@ -1,59 +0,0 @@
using System.ComponentModel.DataAnnotations;
namespace HoneyBox.Admin.Business.Models.User;
/// <summary>
/// VIP等级响应
/// </summary>
public class VipLevelDto
{
/// <summary>
/// ID
/// </summary>
public int Id { get; set; }
/// <summary>
/// VIP等级
/// </summary>
public int Level { get; set; }
/// <summary>
/// 等级名称
/// </summary>
public string Title { get; set; } = string.Empty;
/// <summary>
/// 升级所需数量
/// </summary>
public int Number { get; set; }
/// <summary>
/// 创建时间
/// </summary>
public DateTime CreatedAt { get; set; }
/// <summary>
/// 更新时间
/// </summary>
public DateTime UpdatedAt { get; set; }
}
/// <summary>
/// VIP等级更新请求
/// </summary>
public class VipLevelUpdateRequest
{
/// <summary>
/// 等级名称
/// </summary>
[Required(ErrorMessage = "请输入等级名称")]
[MaxLength(50, ErrorMessage = "等级名称最多50个字符")]
public string Title { get; set; } = string.Empty;
/// <summary>
/// 升级所需数量
/// </summary>
[Required(ErrorMessage = "请输入升级所需数量")]
[Range(0, int.MaxValue, ErrorMessage = "升级所需数量必须大于等于0")]
public int Number { get; set; }
}

View File

@ -93,24 +93,6 @@ public interface IUserBusinessService
#endregion
#region VIP管理
/// <summary>
/// 获取VIP等级列表
/// </summary>
/// <returns>VIP等级列表</returns>
Task<List<VipLevelDto>> GetVipLevelsAsync();
/// <summary>
/// 更新VIP等级
/// </summary>
/// <param name="id">VIP等级ID</param>
/// <param name="request">更新请求</param>
/// <returns>是否成功</returns>
Task<bool> UpdateVipLevelAsync(int id, VipLevelUpdateRequest request);
#endregion
#region
/// <summary>

View File

@ -39,8 +39,8 @@ public class QyLevelService : IQyLevelService
/// <inheritdoc />
public async Task<PagedResult<QyLevelResponse>> GetQyLevelsAsync(QyLevelListRequest request)
{
// 使用 VipLevels 表(与用户端 API 保持一致)
var query = _dbContext.VipLevels
// 使用 EquityLevels 表
var query = _dbContext.EquityLevels
.AsNoTracking()
.Where(e => e.DeletedAt == null && e.Level > 0);
@ -58,7 +58,7 @@ public class QyLevelService : IQyLevelService
.Take(request.PageSize)
.ToListAsync();
var list = items.Select(MapVipLevelToResponse).ToList();
var list = items.Select(MapEquityLevelToResponse).ToList();
return PagedResult<QyLevelResponse>.Create(list, total, request.Page, request.PageSize);
}
@ -67,17 +67,17 @@ public class QyLevelService : IQyLevelService
/// <inheritdoc />
public async Task<QyLevelResponse?> GetQyLevelByIdAsync(int id)
{
var entity = await _dbContext.VipLevels
var entity = await _dbContext.EquityLevels
.AsNoTracking()
.FirstOrDefaultAsync(e => e.Id == id && e.DeletedAt == null);
return entity == null ? null : MapVipLevelToResponse(entity);
return entity == null ? null : MapEquityLevelToResponse(entity);
}
/// <inheritdoc />
public async Task<bool> UpdateQyLevelAsync(int id, QyLevelUpdateRequest request)
{
var entity = await _dbContext.VipLevels
var entity = await _dbContext.EquityLevels
.FirstOrDefaultAsync(e => e.Id == id && e.DeletedAt == null);
if (entity == null)
@ -108,7 +108,7 @@ public class QyLevelService : IQyLevelService
public async Task<PagedResult<QyLevelPrizeResponse>> GetQyLevelPrizesAsync(int qyLevelId, QyLevelPrizeListRequest request)
{
// 验证权益等级是否存在
var qyLevel = await _dbContext.VipLevels
var qyLevel = await _dbContext.EquityLevels
.AsNoTracking()
.FirstOrDefaultAsync(e => e.Id == qyLevelId && e.DeletedAt == null);
@ -117,10 +117,10 @@ public class QyLevelService : IQyLevelService
throw new BusinessException(BusinessErrorCodes.NotFound, "权益等级不存在");
}
// 使用 VipLevelRewards 表
var query = _dbContext.VipLevelRewards
// 使用 EquityLevelRewards 表
var query = _dbContext.EquityLevelRewards
.AsNoTracking()
.Where(p => p.VipLevelId == qyLevelId && p.DeletedAt == null);
.Where(p => p.EquityLevelId == qyLevelId && p.DeletedAt == null);
// 按奖品类型筛选
if (request.Type.HasValue)
@ -157,7 +157,7 @@ public class QyLevelService : IQyLevelService
.ToDictionaryAsync(c => c.Id)
: new Dictionary<int, Coupon>();
var list = items.Select(p => MapVipLevelRewardToResponse(p, qyLevel.Level, coupons)).ToList();
var list = items.Select(p => MapEquityLevelRewardToResponse(p, qyLevel.Level, coupons)).ToList();
return PagedResult<QyLevelPrizeResponse>.Create(list, total, request.Page, request.PageSize);
}
@ -165,7 +165,7 @@ public class QyLevelService : IQyLevelService
/// <inheritdoc />
public async Task<QyLevelPrizeResponse?> GetQyLevelPrizeByIdAsync(int prizeId)
{
var entity = await _dbContext.VipLevelRewards
var entity = await _dbContext.EquityLevelRewards
.AsNoTracking()
.FirstOrDefaultAsync(p => p.Id == prizeId && p.DeletedAt == null);
@ -175,9 +175,9 @@ public class QyLevelService : IQyLevelService
}
// 获取等级信息
var qyLevel = await _dbContext.VipLevels
var qyLevel = await _dbContext.EquityLevels
.AsNoTracking()
.FirstOrDefaultAsync(e => e.Id == entity.VipLevelId);
.FirstOrDefaultAsync(e => e.Id == entity.EquityLevelId);
var level = qyLevel?.Level ?? 0;
// 如果是优惠券类型,获取优惠券信息
@ -193,7 +193,7 @@ public class QyLevelService : IQyLevelService
}
}
return MapVipLevelRewardToResponse(entity, level, coupons);
return MapEquityLevelRewardToResponse(entity, level, coupons);
}
@ -201,7 +201,7 @@ public class QyLevelService : IQyLevelService
public async Task<int> CreateQyLevelPrizeAsync(int qyLevelId, QyLevelPrizeCreateRequest request)
{
// 验证权益等级是否存在
var qyLevel = await _dbContext.VipLevels
var qyLevel = await _dbContext.EquityLevels
.AsNoTracking()
.FirstOrDefaultAsync(e => e.Id == qyLevelId && e.DeletedAt == null);
@ -213,9 +213,9 @@ public class QyLevelService : IQyLevelService
// 验证请求
ValidatePrizeCreateRequest(request);
var entity = new VipLevelReward
var entity = new EquityLevelReward
{
VipLevelId = qyLevelId,
EquityLevelId = qyLevelId,
Type = (byte)request.Type,
Title = request.Title,
CouponId = request.CouponId,
@ -230,7 +230,7 @@ public class QyLevelService : IQyLevelService
UpdatedAt = DateTime.Now
};
_dbContext.VipLevelRewards.Add(entity);
_dbContext.EquityLevelRewards.Add(entity);
await _dbContext.SaveChangesAsync();
_logger.LogInformation("创建权益等级奖品成功: Id={Id}, QyLevelId={QyLevelId}, Type={Type}",
@ -242,7 +242,7 @@ public class QyLevelService : IQyLevelService
/// <inheritdoc />
public async Task<bool> UpdateQyLevelPrizeAsync(int prizeId, QyLevelPrizeUpdateRequest request)
{
var entity = await _dbContext.VipLevelRewards
var entity = await _dbContext.EquityLevelRewards
.FirstOrDefaultAsync(p => p.Id == prizeId && p.DeletedAt == null);
if (entity == null)
@ -275,7 +275,7 @@ public class QyLevelService : IQyLevelService
/// <inheritdoc />
public async Task<bool> DeleteQyLevelPrizeAsync(int prizeId)
{
var entity = await _dbContext.VipLevelRewards
var entity = await _dbContext.EquityLevelRewards
.FirstOrDefaultAsync(p => p.Id == prizeId && p.DeletedAt == null);
if (entity == null)
@ -469,9 +469,9 @@ public class QyLevelService : IQyLevelService
}
/// <summary>
/// 将 VipLevel 实体映射为响应模型
/// 将 EquityLevel 实体映射为响应模型
/// </summary>
private static QyLevelResponse MapVipLevelToResponse(VipLevel entity)
private static QyLevelResponse MapEquityLevelToResponse(EquityLevel entity)
{
return new QyLevelResponse
{
@ -485,15 +485,15 @@ public class QyLevelService : IQyLevelService
}
/// <summary>
/// 将 VipLevelReward 实体映射为奖品响应模型
/// 将 EquityLevelReward 实体映射为奖品响应模型
/// </summary>
private static QyLevelPrizeResponse MapVipLevelRewardToResponse(VipLevelReward entity, int qyLevel, Dictionary<int, Coupon> coupons)
private static QyLevelPrizeResponse MapEquityLevelRewardToResponse(EquityLevelReward entity, int qyLevel, Dictionary<int, Coupon> coupons)
{
var typeValue = (int)entity.Type;
var response = new QyLevelPrizeResponse
{
Id = entity.Id,
QyLevelId = entity.VipLevelId,
QyLevelId = entity.EquityLevelId,
QyLevel = qyLevel,
Type = typeValue,
TypeName = PrizeTypeNames.GetValueOrDefault(typeValue, "未知"),

View File

@ -88,7 +88,7 @@ public class UserBusinessService : IUserBusinessService
Balance = user.Money,
Integral = user.Integral,
Diamond = user.Score,
VipLevel = user.Vip,
EquityLevel = user.Vip,
CreatedAt = user.CreatedAt,
LastLoginIp = loginIps.GetValueOrDefault(userId),
Status = user.Status,
@ -508,52 +508,6 @@ public class UserBusinessService : IUserBusinessService
#endregion
#region VIP管理
/// <inheritdoc />
public async Task<List<VipLevelDto>> GetVipLevelsAsync()
{
var vipLevels = await _dbContext.VipLevels
.AsNoTracking()
.Where(v => v.DeletedAt == null)
.OrderBy(v => v.Level)
.Select(v => new VipLevelDto
{
Id = v.Id,
Level = v.Level,
Title = v.Title,
Number = v.Number,
CreatedAt = v.CreatedAt,
UpdatedAt = v.UpdatedAt
})
.ToListAsync();
return vipLevels;
}
/// <inheritdoc />
public async Task<bool> UpdateVipLevelAsync(int id, VipLevelUpdateRequest request)
{
var vipLevel = await _dbContext.VipLevels.FirstOrDefaultAsync(v => v.Id == id && v.DeletedAt == null);
if (vipLevel == null)
{
throw new BusinessException(BusinessErrorCodes.NotFound, "VIP等级不存在");
}
vipLevel.Title = request.Title;
vipLevel.Number = request.Number;
vipLevel.UpdatedAt = DateTime.Now;
var result = await _dbContext.SaveChangesAsync() > 0;
_logger.LogInformation("更新VIP等级: Id={Id}, Title={Title}, Number={Number}", id, request.Title, request.Number);
return result;
}
#endregion
#region
/// <inheritdoc />
@ -1269,7 +1223,7 @@ public class UserBusinessService : IUserBusinessService
Balance = user.Money,
Integral = user.Integral,
Diamond = user.Score,
VipLevel = user.Vip,
EquityLevel = user.Vip,
CreatedAt = user.CreatedAt,
LastLoginIp = loginIps.GetValueOrDefault(user.Id),
Status = user.Status,

View File

@ -3,17 +3,17 @@ using HoneyBox.Core.Interfaces;
using HoneyBox.Model.Base;
using HoneyBox.Model.Models.Asset;
using HoneyBox.Model.Models.Auth;
using HoneyBox.Model.Models.Vip;
using HoneyBox.Model.Models.Equity;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace HoneyBox.Api.Controllers;
/// <summary>
/// 用户控制器 - 处理用户信息、资产和VIP相关功能
/// 用户控制器 - 处理用户信息、资产和权益相关功能
/// </summary>
/// <remarks>
/// 提供用户信息查询和更新、资产明细查询、VIP信息查询等功能
/// 提供用户信息查询和更新、资产明细查询、权益信息查询等功能
/// </remarks>
[ApiController]
[Route("api")]
@ -22,7 +22,7 @@ public class UserController : ControllerBase
private readonly IUserService _userService;
private readonly IAuthService _authService;
private readonly IAssetService _assetService;
private readonly IVipService _vipService;
private readonly IEquityService _equityService;
private readonly IQuanYiService _quanYiService;
private readonly IImageUploadService _imageUploadService;
private readonly ILogger<UserController> _logger;
@ -31,7 +31,7 @@ public class UserController : ControllerBase
IUserService userService,
IAuthService authService,
IAssetService assetService,
IVipService vipService,
IEquityService equityService,
IQuanYiService quanYiService,
IImageUploadService imageUploadService,
ILogger<UserController> logger)
@ -39,7 +39,7 @@ public class UserController : ControllerBase
_userService = userService;
_authService = authService;
_assetService = assetService;
_vipService = vipService;
_equityService = equityService;
_quanYiService = quanYiService;
_imageUploadService = imageUploadService;
_logger = logger;
@ -384,50 +384,50 @@ public class UserController : ControllerBase
#endregion
#region VIP Endpoints
#region Equity Endpoints
/// <summary>
/// 获取VIP信息
/// 获取权益等级信息
/// </summary>
/// <remarks>
/// POST /api/vip_list
/// GET /api/equity_list
///
/// 获取当前用户的VIP等级信息和权益列表
/// 获取当前用户的权益等级信息和权益列表
/// Requirements: 2.1-2.5
/// </remarks>
/// <returns>VIP信息数据</returns>
[HttpGet("vip_list")]
/// <returns>权益等级信息数据</returns>
[HttpGet("equity_list")]
[Authorize]
[ProducesResponseType(typeof(ApiResponse<VipInfoResponse>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ApiResponse<VipInfoResponse>), StatusCodes.Status401Unauthorized)]
public async Task<ApiResponse<VipInfoResponse>> GetVipInfo()
[ProducesResponseType(typeof(ApiResponse<EquityInfoResponse>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ApiResponse<EquityInfoResponse>), StatusCodes.Status401Unauthorized)]
public async Task<ApiResponse<EquityInfoResponse>> GetEquityInfo()
{
var userId = GetCurrentUserId();
if (userId == null)
{
return ApiResponse<VipInfoResponse>.Unauthorized();
return ApiResponse<EquityInfoResponse>.Unauthorized();
}
try
{
var result = await _vipService.GetVipInfoAsync(userId.Value);
return ApiResponse<VipInfoResponse>.Success(result);
var result = await _equityService.GetEquityInfoAsync(userId.Value);
return ApiResponse<EquityInfoResponse>.Success(result);
}
catch (InvalidOperationException ex)
{
_logger.LogWarning("Get VIP info failed: UserId={UserId}, Error={Error}", userId, ex.Message);
return ApiResponse<VipInfoResponse>.Fail(ex.Message);
_logger.LogWarning("Get equity info failed: UserId={UserId}, Error={Error}", userId, ex.Message);
return ApiResponse<EquityInfoResponse>.Fail(ex.Message);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to get VIP info: UserId={UserId}", userId);
return ApiResponse<VipInfoResponse>.Fail("获取VIP信息失败");
_logger.LogError(ex, "Failed to get equity info: UserId={UserId}", userId);
return ApiResponse<EquityInfoResponse>.Fail("获取权益信息失败");
}
}
#endregion
#region QuanYi (VIP Rights) Endpoints
#region QuanYi (Equity Rights) Endpoints
/// <summary>
/// 获取权益中心信息

View File

@ -0,0 +1,30 @@
using HoneyBox.Model.Models.Equity;
namespace HoneyBox.Core.Interfaces;
/// <summary>
/// 权益服务接口
/// </summary>
public interface IEquityService
{
/// <summary>
/// 获取权益信息
/// </summary>
/// <param name="userId">用户ID</param>
/// <returns>权益信息响应</returns>
Task<EquityInfoResponse> GetEquityInfoAsync(int userId);
/// <summary>
/// 计算权益等级
/// </summary>
/// <param name="userId">用户ID</param>
/// <param name="currentLevel">当前权益等级</param>
/// <returns>计算后的权益等级</returns>
Task<int> CalculateEquityLevelAsync(int userId, int currentLevel);
/// <summary>
/// 获取权益等级列表
/// </summary>
/// <returns>权益等级列表</returns>
Task<List<EquityLevelDto>> GetEquityLevelsAsync();
}

View File

@ -1,4 +1,4 @@
using HoneyBox.Model.Models.Vip;
using HoneyBox.Model.Models.Equity;
namespace HoneyBox.Core.Interfaces;

View File

@ -1,30 +0,0 @@
using HoneyBox.Model.Models.Vip;
namespace HoneyBox.Core.Interfaces;
/// <summary>
/// VIP服务接口
/// </summary>
public interface IVipService
{
/// <summary>
/// 获取VIP信息
/// </summary>
/// <param name="userId">用户ID</param>
/// <returns>VIP信息响应</returns>
Task<VipInfoResponse> GetVipInfoAsync(int userId);
/// <summary>
/// 计算VIP等级
/// </summary>
/// <param name="userId">用户ID</param>
/// <param name="currentVip">当前VIP等级</param>
/// <returns>计算后的VIP等级</returns>
Task<int> CalculateVipLevelAsync(int userId, int currentVip);
/// <summary>
/// 获取VIP等级列表
/// </summary>
/// <returns>VIP等级列表</returns>
Task<List<VipLevelDto>> GetVipLevelsAsync();
}

View File

@ -1,28 +1,28 @@
using HoneyBox.Core.Interfaces;
using HoneyBox.Model.Data;
using HoneyBox.Model.Entities;
using HoneyBox.Model.Models.Vip;
using HoneyBox.Model.Models.Equity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
namespace HoneyBox.Core.Services;
/// <summary>
/// VIP服务实现
/// 权益服务实现
/// </summary>
public class VipService : IVipService
public class EquityService : IEquityService
{
private readonly HoneyBoxDbContext _dbContext;
private readonly ILogger<VipService> _logger;
private readonly ILogger<EquityService> _logger;
public VipService(HoneyBoxDbContext dbContext, ILogger<VipService> logger)
public EquityService(HoneyBoxDbContext dbContext, ILogger<EquityService> logger)
{
_dbContext = dbContext;
_logger = logger;
}
/// <inheritdoc />
public async Task<VipInfoResponse> GetVipInfoAsync(int userId)
public async Task<EquityInfoResponse> GetEquityInfoAsync(int userId)
{
// 获取用户信息
var user = await _dbContext.Users
@ -35,24 +35,24 @@ public class VipService : IVipService
throw new InvalidOperationException("用户不存在");
}
// 计算并更新VIP等级
var currentVip = await CalculateVipLevelAsync(userId, user.Vip);
// 计算并更新权益等级
var currentLevel = await CalculateEquityLevelAsync(userId, user.Vip);
// 获取所有VIP等级列表
var vipLevels = await _dbContext.VipLevels
// 获取所有权益等级列表
var equityLevels = await _dbContext.EquityLevels
.Where(v => v.DeletedAt == null)
.OrderBy(v => v.Level)
.ToListAsync();
// 获取最大VIP等级
var maxVipLevel = vipLevels.Any() ? vipLevels.Max(v => v.Level) : 0;
// 获取最大权益等级
var maxLevel = equityLevels.Any() ? equityLevels.Max(v => v.Level) : 0;
// 构建用户VIP信息
var userInfo = new VipUserInfoDto
// 构建用户权益信息
var userInfo = new EquityUserInfoDto
{
Nickname = user.Nickname,
Headimg = user.HeadImg,
Vip = currentVip,
Vip = currentLevel,
UpgradeMoney = "0",
LastVip = 0,
JinDu = 0,
@ -60,14 +60,14 @@ public class VipService : IVipService
};
// 如果未达到最高等级,计算升级进度
if (currentVip < maxVipLevel)
if (currentLevel < maxLevel)
{
// 计算累计消费
var totalOrder = await GetTotalOrderConsumptionAsync(userId);
// 获取下一个VIP等级的条件
var nextLevel = vipLevels
.Where(v => v.Level > currentVip)
// 获取下一个权益等级的条件
var nextLevel = equityLevels
.Where(v => v.Level > currentLevel)
.OrderBy(v => v.Level)
.FirstOrDefault();
@ -96,72 +96,72 @@ public class VipService : IVipService
}
// 获取当前等级的提示信息
var currentLevelInfo = vipLevels.FirstOrDefault(v => v.Level == currentVip);
var currentLevelInfo = equityLevels.FirstOrDefault(v => v.Level == currentLevel);
if (currentLevelInfo != null)
{
userInfo.Notice = currentLevelInfo.Title; // 使用标题作为提示信息
userInfo.Notice = currentLevelInfo.Title;
}
// 构建VIP等级列表DTO
var vipLevelDtos = vipLevels.Select(v => new VipLevelDto
// 构建权益等级列表DTO
var levelDtos = equityLevels.Select(v => new EquityLevelDto
{
Id = v.Id,
Level = v.Level,
Title = v.Title,
Imgurl = string.Empty, // SQL Server表中没有imgurl字段
Imgurl = string.Empty,
Condition = v.Number,
Discount = 0, // SQL Server表中没有discount字段
Discount = 0,
Notice = v.Title
}).ToList();
return new VipInfoResponse
return new EquityInfoResponse
{
Userinfo = userInfo,
Data = vipLevelDtos
Data = levelDtos
};
}
/// <inheritdoc />
public async Task<int> CalculateVipLevelAsync(int userId, int currentVip)
public async Task<int> CalculateEquityLevelAsync(int userId, int currentLevel)
{
// 计算累计消费
var totalOrder = await GetTotalOrderConsumptionAsync(userId);
// 查找符合升级条件的最高等级
// 条件:等级 > 当前等级 且 升级条件 <= 累计消费
var newVipLevel = await _dbContext.VipLevels
var newLevel = await _dbContext.EquityLevels
.Where(v => v.DeletedAt == null)
.Where(v => v.Level > currentVip)
.Where(v => v.Level > currentLevel)
.Where(v => v.Number <= totalOrder)
.OrderByDescending(v => v.Level)
.FirstOrDefaultAsync();
if (newVipLevel != null)
if (newLevel != null)
{
// 更新用户VIP等级
// 更新用户权益等级
var user = await _dbContext.Users.FindAsync(userId);
if (user != null)
{
user.Vip = (byte)newVipLevel.Level;
user.Vip = (byte)newLevel.Level;
await _dbContext.SaveChangesAsync();
_logger.LogInformation("用户 {UserId} VIP等级从 {OldVip} 升级到 {NewVip}",
userId, currentVip, newVipLevel.Level);
_logger.LogInformation("用户 {UserId} 权益等级从 {OldLevel} 升级到 {NewLevel}",
userId, currentLevel, newLevel.Level);
}
return newVipLevel.Level;
return newLevel.Level;
}
return currentVip;
return currentLevel;
}
/// <inheritdoc />
public async Task<List<VipLevelDto>> GetVipLevelsAsync()
public async Task<List<EquityLevelDto>> GetEquityLevelsAsync()
{
var vipLevels = await _dbContext.VipLevels
var equityLevels = await _dbContext.EquityLevels
.Where(v => v.DeletedAt == null)
.OrderBy(v => v.Level)
.ToListAsync();
return vipLevels.Select(v => new VipLevelDto
return equityLevels.Select(v => new EquityLevelDto
{
Id = v.Id,
Level = v.Level,
@ -176,8 +176,6 @@ public class VipService : IVipService
/// <summary>
/// 获取用户累计订单消费金额
/// </summary>
/// <param name="userId">用户ID</param>
/// <returns>累计消费金额</returns>
private async Task<decimal> GetTotalOrderConsumptionAsync(int userId)
{
// 计算累计消费status=1已支付且 order_type<5

View File

@ -1,7 +1,7 @@
using HoneyBox.Core.Interfaces;
using HoneyBox.Model.Data;
using HoneyBox.Model.Entities;
using HoneyBox.Model.Models.Vip;
using HoneyBox.Model.Models.Equity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
@ -33,7 +33,7 @@ public class QuanYiService : IQuanYiService
}
// 从数据库获取等级配置
var vipLevelConfigs = await _dbContext.VipLevels
var equityLevelConfigs = await _dbContext.EquityLevels
.Where(v => v.DeletedAt == null)
.OrderBy(v => v.Level)
.ToListAsync();
@ -41,28 +41,28 @@ public class QuanYiService : IQuanYiService
var response = new QuanYiResponse
{
UserImg = GetImageUrl(user.HeadImg),
QuanYiLevel = GetQuanYiLevelInfo(user.OuQiLevel ?? 0, user.OuQi ?? 0, vipLevelConfigs)
QuanYiLevel = GetQuanYiLevelInfo(user.OuQiLevel ?? 0, user.OuQi ?? 0, equityLevelConfigs)
};
// Get all VIP levels with level > 0
var vipLevels = await _dbContext.VipLevels
// Get all equity levels with level > 0
var equityLevels = await _dbContext.EquityLevels
.Where(v => v.Level > 0 && v.DeletedAt == null)
.OrderBy(v => v.Level)
.ToListAsync();
var levelList = new List<QuanYiLevelDto>();
foreach (var vipLevel in vipLevels)
foreach (var equityLevel in equityLevels)
{
var levelDto = new QuanYiLevelDto
{
Id = vipLevel.Id,
Level = vipLevel.Level
Id = equityLevel.Id,
Level = equityLevel.Level
};
// Get coupon rewards (type = 1)
var puJiang = await _dbContext.VipLevelRewards
.Where(r => r.VipLevelId == vipLevel.Id && r.Type == 1 && r.DeletedAt == null)
var puJiang = await _dbContext.EquityLevelRewards
.Where(r => r.EquityLevelId == equityLevel.Id && r.Type == 1 && r.DeletedAt == null)
.OrderBy(r => r.Id)
.Select(r => new QuanYiRewardDto
{
@ -75,8 +75,8 @@ public class QuanYiService : IQuanYiService
levelDto.PuJiang = puJiang;
// Get prize rewards (type = 2)
var gaoJiangRaw = await _dbContext.VipLevelRewards
.Where(r => r.VipLevelId == vipLevel.Id && r.Type == 2 && r.DeletedAt == null)
var gaoJiangRaw = await _dbContext.EquityLevelRewards
.Where(r => r.EquityLevelId == equityLevel.Id && r.Type == 2 && r.DeletedAt == null)
.OrderBy(r => r.Id)
.Select(r => new QuanYiRewardDto
{
@ -94,7 +94,7 @@ public class QuanYiService : IQuanYiService
levelDto.GaoJiang = gaoJiangRaw;
// Check if user can claim this level's rewards
if ((user.OuQiLevel ?? 0) >= vipLevel.Level)
if ((user.OuQiLevel ?? 0) >= equityLevel.Level)
{
levelDto.IsLing = 1; // Can claim
}
@ -104,9 +104,9 @@ public class QuanYiService : IQuanYiService
}
// Check if user has already claimed this level's rewards
var hasClaimed = await _dbContext.UserVipRewards
.AnyAsync(r => r.VipLevelId == vipLevel.Id
&& r.VipLevel == vipLevel.Level
var hasClaimed = await _dbContext.UserEquityRewards
.AnyAsync(r => r.EquityLevelId == equityLevel.Id
&& r.EquityLevel == equityLevel.Level
&& r.UserId == userId
&& r.DeletedAt == null);
@ -144,21 +144,21 @@ public class QuanYiService : IQuanYiService
throw new InvalidOperationException("用户不存在");
}
var vipLevel = await _dbContext.VipLevels
var equityLevel = await _dbContext.EquityLevels
.FirstOrDefaultAsync(v => v.Id == levelId && v.DeletedAt == null);
if (vipLevel == null)
if (equityLevel == null)
{
throw new InvalidOperationException("数据错误");
}
if (vipLevel.Level > (user.OuQiLevel ?? 0))
if (equityLevel.Level > (user.OuQiLevel ?? 0))
{
throw new InvalidOperationException("暂未达到该等级");
}
// Check if rewards exist for this level
var hasRewards = await _dbContext.VipLevelRewards
.AnyAsync(r => r.VipLevelId == vipLevel.Id && r.DeletedAt == null);
var hasRewards = await _dbContext.EquityLevelRewards
.AnyAsync(r => r.EquityLevelId == equityLevel.Id && r.DeletedAt == null);
if (!hasRewards)
{
throw new InvalidOperationException("奖品不存在");
@ -172,8 +172,8 @@ public class QuanYiService : IQuanYiService
try
{
// Process coupon rewards (type = 1)
var couponRewards = await _dbContext.VipLevelRewards
.Where(r => r.VipLevelId == vipLevel.Id && r.Type == 1 && r.DeletedAt == null)
var couponRewards = await _dbContext.EquityLevelRewards
.Where(r => r.EquityLevelId == equityLevel.Id && r.Type == 1 && r.DeletedAt == null)
.ToListAsync();
foreach (var reward in couponRewards)
@ -223,12 +223,12 @@ public class QuanYiService : IQuanYiService
_dbContext.CouponReceives.Add(couponReceive);
}
// Record user vip reward
var userVipReward = new UserVipReward
// Record user equity reward
var userEquityReward = new UserEquityReward
{
UserId = userId,
VipLevelId = vipLevel.Id,
VipLevel = vipLevel.Level,
EquityLevelId = equityLevel.Id,
EquityLevel = equityLevel.Level,
CouponId = reward.CouponId,
Type = 1,
Title = reward.Title,
@ -240,18 +240,18 @@ public class QuanYiService : IQuanYiService
CreatedAt = DateTime.UtcNow,
UpdatedAt = DateTime.UtcNow
};
_dbContext.UserVipRewards.Add(userVipReward);
_dbContext.UserEquityRewards.Add(userEquityReward);
}
// Process prize rewards (type = 2) with probability
var prizeRewards = await _dbContext.VipLevelRewards
.Where(r => r.VipLevelId == vipLevel.Id && r.Type == 2 && r.Probability > 0 && r.DeletedAt == null)
var prizeRewards = await _dbContext.EquityLevelRewards
.Where(r => r.EquityLevelId == equityLevel.Id && r.Type == 2 && r.Probability > 0 && r.DeletedAt == null)
.ToListAsync();
if (prizeRewards.Any())
{
// Build probability pool
var probabilityPool = new List<VipLevelReward>();
var probabilityPool = new List<EquityLevelReward>();
foreach (var prize in prizeRewards)
{
var count = (int)((prize.Probability ?? 0) * 100);
@ -327,12 +327,12 @@ public class QuanYiService : IQuanYiService
};
_dbContext.OrderItems.Add(orderItem);
// Record user vip reward for prize
var userVipReward = new UserVipReward
// Record user equity reward for prize
var userEquityReward = new UserEquityReward
{
UserId = userId,
VipLevelId = vipLevel.Id,
VipLevel = vipLevel.Level,
EquityLevelId = equityLevel.Id,
EquityLevel = equityLevel.Level,
Type = 2,
Title = selectedPrize.Title,
ImgUrl = selectedPrize.ImgUrl,
@ -345,20 +345,20 @@ public class QuanYiService : IQuanYiService
CreatedAt = DateTime.UtcNow,
UpdatedAt = DateTime.UtcNow
};
_dbContext.UserVipRewards.Add(userVipReward);
_dbContext.UserEquityRewards.Add(userEquityReward);
}
}
await _dbContext.SaveChangesAsync();
await transaction.CommitAsync();
_logger.LogInformation("User {UserId} claimed VIP level {LevelId} rewards", userId, levelId);
_logger.LogInformation("User {UserId} claimed equity level {LevelId} rewards", userId, levelId);
return new QuanYiLingResponse(rewards);
}
catch (Exception ex)
{
await transaction.RollbackAsync();
_logger.LogError(ex, "Failed to claim VIP rewards for user {UserId}, level {LevelId}", userId, levelId);
_logger.LogError(ex, "Failed to claim equity rewards for user {UserId}, level {LevelId}", userId, levelId);
throw new InvalidOperationException("网络故障,请稍后再试");
}
}
@ -380,14 +380,14 @@ public class QuanYiService : IQuanYiService
/// <summary>
/// Get QuanYi level info based on ou_qi_level and ou_qi
/// </summary>
private QuanYiLevelInfo GetQuanYiLevelInfo(int ouQiLevel, int ouQi, List<VipLevel> vipLevelConfigs)
private QuanYiLevelInfo GetQuanYiLevelInfo(int ouQiLevel, int ouQi, List<EquityLevel> equityLevelConfigs)
{
// 从数据库配置构建等级阈值
var levelThresholds = vipLevelConfigs
var levelThresholds = equityLevelConfigs
.ToDictionary(v => v.Level, v => v.Number);
// 构建等级标题
var levelTitles = vipLevelConfigs
var levelTitles = equityLevelConfigs
.ToDictionary(v => v.Level, v => v.Title ?? $"等级{v.Level}");
// 如果没有配置,使用默认值

View File

@ -156,7 +156,7 @@ public class UserService : BaseService<User, int>, IUserService
Integral = user.Integral,
Score = user.Score,
Vip = user.Vip,
VipImgurl = GetVipImageUrl(user.Vip),
VipImgurl = GetEquityImageUrl(user.Vip),
Coupon = couponCount,
Day = registrationDays,
QuanYiLevel = quanYiLevel
@ -221,24 +221,24 @@ public class UserService : BaseService<User, int>, IUserService
if (user == null)
return currentVip;
// Get all VIP levels ordered by level
var vipLevels = await _dbContext.VipLevels
// Get all equity levels ordered by level
var equityLevels = await _dbContext.EquityLevels
.Where(v => v.DeletedAt == null)
.OrderBy(v => v.Level)
.ToListAsync();
if (!vipLevels.Any())
if (!equityLevels.Any())
return currentVip;
// Calculate VIP level based on total spending (Money)
var totalSpending = user.Money;
int newVipLevel = 0;
foreach (var vipLevel in vipLevels)
foreach (var equityLevel in equityLevels)
{
if (totalSpending >= vipLevel.Number)
if (totalSpending >= equityLevel.Number)
{
newVipLevel = vipLevel.Level;
newVipLevel = equityLevel.Level;
}
else
{
@ -381,14 +381,14 @@ public class UserService : BaseService<User, int>, IUserService
}
/// <summary>
/// Get VIP image URL based on VIP level
/// Get equity image URL based on equity level
/// </summary>
private string? GetVipImageUrl(int vipLevel)
private string? GetEquityImageUrl(int equityLevel)
{
if (vipLevel <= 0)
if (equityLevel <= 0)
return null;
// Return a placeholder URL - actual URLs should be configured
return $"https://example.com/vip/level_{vipLevel}.png";
return $"https://example.com/equity/level_{equityLevel}.png";
}
}

View File

@ -73,13 +73,13 @@ public class ServiceModule : Module
return new AssetService(dbContext, logger);
}).As<IAssetService>().InstancePerLifetimeScope();
// 注册VIP服务
// 注册权益服务
builder.Register(c =>
{
var dbContext = c.Resolve<HoneyBoxDbContext>();
var logger = c.Resolve<ILogger<VipService>>();
return new VipService(dbContext, logger);
}).As<IVipService>().InstancePerLifetimeScope();
var logger = c.Resolve<ILogger<EquityService>>();
return new EquityService(dbContext, logger);
}).As<IEquityService>().InstancePerLifetimeScope();
// 注册优惠券服务
builder.Register(c =>

View File

@ -88,11 +88,11 @@ public partial class HoneyBoxDbContext : DbContext
public virtual DbSet<UserTask> UserTasks { get; set; }
public virtual DbSet<UserVipReward> UserVipRewards { get; set; }
public virtual DbSet<UserEquityReward> UserEquityRewards { get; set; }
public virtual DbSet<VipLevel> VipLevels { get; set; }
public virtual DbSet<EquityLevel> EquityLevels { get; set; }
public virtual DbSet<VipLevelReward> VipLevelRewards { get; set; }
public virtual DbSet<EquityLevelReward> EquityLevelRewards { get; set; }
public virtual DbSet<RedeemCode> RedeemCodes { get; set; }
@ -118,10 +118,6 @@ public partial class HoneyBoxDbContext : DbContext
public virtual DbSet<RankMonth> RankMonths { get; set; }
public virtual DbSet<EquityLevel> EquityLevels { get; set; }
public virtual DbSet<EquityLevelPrize> EquityLevelPrizes { get; set; }
public virtual DbSet<GoodsKingRank> GoodsKingRanks { get; set; }
public virtual DbSet<UserRefreshToken> UserRefreshTokens { get; set; }
@ -2459,21 +2455,21 @@ public partial class HoneyBoxDbContext : DbContext
.HasColumnName("z_number");
});
modelBuilder.Entity<UserVipReward>(entity =>
modelBuilder.Entity<UserEquityReward>(entity =>
{
entity.HasKey(e => e.Id).HasName("pk_user_vip_rewards");
entity.HasKey(e => e.Id).HasName("pk_user_equity_rewards");
entity.ToTable("user_vip_rewards", tb => tb.HasComment("用户领取的VIP奖品表"));
entity.ToTable("user_equity_rewards", tb => tb.HasComment("用户领取的权益奖品表"));
entity.HasIndex(e => e.Type, "ix_user_vip_rewards_type");
entity.HasIndex(e => e.Type, "ix_user_equity_rewards_type");
entity.HasIndex(e => e.UserId, "ix_user_vip_rewards_user_id");
entity.HasIndex(e => e.UserId, "ix_user_equity_rewards_user_id");
entity.HasIndex(e => new { e.UserId, e.VipLevel }, "ix_user_vip_rewards_user_level");
entity.HasIndex(e => new { e.UserId, e.EquityLevel }, "ix_user_equity_rewards_user_level");
entity.HasIndex(e => e.VipLevel, "ix_user_vip_rewards_vip_level");
entity.HasIndex(e => e.EquityLevel, "ix_user_equity_rewards_equity_level");
entity.HasIndex(e => e.VipLevelId, "ix_user_vip_rewards_vip_level_id");
entity.HasIndex(e => e.EquityLevelId, "ix_user_equity_rewards_equity_level_id");
entity.Property(e => e.Id)
.HasComment("主键ID")
@ -2549,26 +2545,26 @@ public partial class HoneyBoxDbContext : DbContext
entity.Property(e => e.UserId)
.HasComment("用户ID")
.HasColumnName("user_id");
entity.Property(e => e.VipLevel)
entity.Property(e => e.EquityLevel)
.HasDefaultValue(0)
.HasComment("VIP等级")
.HasColumnName("vip_level");
entity.Property(e => e.VipLevelId)
.HasComment("VIP等级ID")
.HasColumnName("vip_level_id");
.HasComment("权益等级")
.HasColumnName("equity_level");
entity.Property(e => e.EquityLevelId)
.HasComment("权益等级ID")
.HasColumnName("equity_level_id");
entity.Property(e => e.ZNum)
.HasDefaultValue(0)
.HasComment("数量")
.HasColumnName("z_num");
});
modelBuilder.Entity<VipLevel>(entity =>
modelBuilder.Entity<EquityLevel>(entity =>
{
entity.HasKey(e => e.Id).HasName("pk_vip_levels");
entity.HasKey(e => e.Id).HasName("pk_equity_levels");
entity.ToTable("vip_levels", tb => tb.HasComment("VIP等级配置表"));
entity.ToTable("equity_levels", tb => tb.HasComment("权益等级配置表"));
entity.HasIndex(e => e.Level, "ix_vip_levels_level");
entity.HasIndex(e => e.Level, "ix_equity_levels_level");
entity.Property(e => e.Id)
.HasComment("主键ID")
@ -2581,10 +2577,10 @@ public partial class HoneyBoxDbContext : DbContext
.HasComment("删除时间")
.HasColumnName("deleted_at");
entity.Property(e => e.Level)
.HasComment("VIP等级")
.HasComment("权益等级")
.HasColumnName("level");
entity.Property(e => e.Number)
.HasComment("升级所需数量")
.HasComment("升级所需欧气值")
.HasColumnName("number");
entity.Property(e => e.Title)
.HasMaxLength(255)
@ -2596,19 +2592,19 @@ public partial class HoneyBoxDbContext : DbContext
.HasColumnName("updated_at");
});
modelBuilder.Entity<VipLevelReward>(entity =>
modelBuilder.Entity<EquityLevelReward>(entity =>
{
entity.HasKey(e => e.Id).HasName("pk_vip_level_rewards");
entity.HasKey(e => e.Id).HasName("pk_equity_level_rewards");
entity.ToTable("vip_level_rewards", tb => tb.HasComment("VIP等级奖品配置表"));
entity.ToTable("equity_level_rewards", tb => tb.HasComment("权益等级奖品配置表"));
entity.HasIndex(e => e.CouponId, "ix_vip_level_rewards_coupon_id");
entity.HasIndex(e => e.CouponId, "ix_equity_level_rewards_coupon_id");
entity.HasIndex(e => e.Type, "ix_vip_level_rewards_type");
entity.HasIndex(e => e.Type, "ix_equity_level_rewards_type");
entity.HasIndex(e => e.VipLevel, "ix_vip_level_rewards_vip_level");
entity.HasIndex(e => e.EquityLevel, "ix_equity_level_rewards_equity_level");
entity.HasIndex(e => e.VipLevelId, "ix_vip_level_rewards_vip_level_id");
entity.HasIndex(e => e.EquityLevelId, "ix_equity_level_rewards_equity_level_id");
entity.Property(e => e.Id)
.HasComment("主键ID")
@ -2679,13 +2675,13 @@ public partial class HoneyBoxDbContext : DbContext
.HasDefaultValueSql("(getdate())")
.HasComment("更新时间")
.HasColumnName("updated_at");
entity.Property(e => e.VipLevel)
entity.Property(e => e.EquityLevel)
.HasDefaultValue(0)
.HasComment("VIP等级")
.HasColumnName("vip_level");
entity.Property(e => e.VipLevelId)
.HasComment("VIP等级ID")
.HasColumnName("vip_level_id");
.HasComment("权益等级")
.HasColumnName("equity_level");
entity.Property(e => e.EquityLevelId)
.HasComment("权益等级ID")
.HasColumnName("equity_level_id");
entity.Property(e => e.ZNum)
.HasDefaultValue(0)
.HasComment("数量")
@ -3240,145 +3236,6 @@ public partial class HoneyBoxDbContext : DbContext
.HasColumnName("prize_imgurl");
});
modelBuilder.Entity<EquityLevel>(entity =>
{
entity.HasKey(e => e.Id).HasName("pk_equity_levels");
entity.ToTable("equity_levels", tb => tb.HasComment("权益等级表,存储用户权益等级配置"));
entity.HasIndex(e => e.Level, "ix_equity_levels_level");
entity.HasIndex(e => e.DeletedAt, "ix_equity_levels_deleted_at");
entity.Property(e => e.Id)
.HasComment("主键ID")
.HasColumnName("id");
entity.Property(e => e.Level)
.HasComment("等级")
.HasColumnName("level");
entity.Property(e => e.Title)
.HasMaxLength(100)
.HasComment("等级名称")
.HasColumnName("title");
entity.Property(e => e.RequiredPoints)
.HasDefaultValue(0)
.HasComment("所需欧气值")
.HasColumnName("required_points");
entity.Property(e => e.CreatedAt)
.HasDefaultValueSql("(getdate())")
.HasComment("创建时间")
.HasColumnName("created_at");
entity.Property(e => e.UpdatedAt)
.HasDefaultValueSql("(getdate())")
.HasComment("更新时间")
.HasColumnName("updated_at");
entity.Property(e => e.DeletedAt)
.HasComment("删除时间(软删除)")
.HasColumnName("deleted_at");
});
modelBuilder.Entity<EquityLevelPrize>(entity =>
{
entity.HasKey(e => e.Id).HasName("pk_equity_level_prizes");
entity.ToTable("equity_level_prizes", tb => tb.HasComment("权益等级奖品表,存储各等级对应的奖品配置"));
entity.HasIndex(e => e.QyLevelId, "ix_equity_level_prizes_qy_level_id");
entity.HasIndex(e => e.Type, "ix_equity_level_prizes_type");
entity.HasIndex(e => e.DeletedAt, "ix_equity_level_prizes_deleted_at");
entity.HasIndex(e => e.Sort, "ix_equity_level_prizes_sort");
entity.Property(e => e.Id)
.HasComment("主键ID")
.HasColumnName("id");
entity.Property(e => e.QyLevelId)
.HasComment("权益等级ID")
.HasColumnName("qy_level_id");
entity.Property(e => e.QyLevel)
.HasComment("权益等级")
.HasColumnName("qy_level");
entity.Property(e => e.Type)
.HasDefaultValue((byte)2)
.HasComment("奖品类型1-优惠券 2-实物奖品")
.HasColumnName("type");
entity.Property(e => e.Title)
.HasMaxLength(255)
.HasComment("奖品标题")
.HasColumnName("title");
entity.Property(e => e.CouponId)
.HasComment("优惠券ID")
.HasColumnName("coupon_id");
entity.Property(e => e.ZNum)
.HasDefaultValue(0)
.HasComment("优惠券数量")
.HasColumnName("z_num");
entity.Property(e => e.ManPrice)
.HasDefaultValue(0m)
.HasComment("满减门槛")
.HasColumnType("decimal(10, 2)")
.HasColumnName("man_price");
entity.Property(e => e.JianPrice)
.HasDefaultValue(0m)
.HasComment("减免金额")
.HasColumnType("decimal(10, 2)")
.HasColumnName("jian_price");
entity.Property(e => e.EffectiveDay)
.HasDefaultValue(0)
.HasComment("有效天数")
.HasColumnName("effective_day");
entity.Property(e => e.JiangPrice)
.HasDefaultValue(0m)
.HasComment("奖品价值")
.HasColumnType("decimal(10, 2)")
.HasColumnName("jiang_price");
entity.Property(e => e.Money)
.HasDefaultValue(0m)
.HasComment("兑换价格")
.HasColumnType("decimal(10, 2)")
.HasColumnName("money");
entity.Property(e => e.ScMoney)
.HasDefaultValue(0m)
.HasComment("市场参考价")
.HasColumnType("decimal(10, 2)")
.HasColumnName("sc_money");
entity.Property(e => e.Probability)
.HasDefaultValue(0m)
.HasComment("中奖概率0-100")
.HasColumnType("decimal(5, 2)")
.HasColumnName("probability");
entity.Property(e => e.ImgUrl)
.HasMaxLength(500)
.HasComment("奖品图片URL")
.HasColumnName("imgurl");
entity.Property(e => e.PrizeCode)
.HasMaxLength(100)
.HasComment("奖品编码")
.HasColumnName("prize_code");
entity.Property(e => e.ShangId)
.HasComment("奖品等级ID")
.HasColumnName("shang_id");
entity.Property(e => e.Sort)
.HasDefaultValue(0)
.HasComment("排序")
.HasColumnName("sort");
entity.Property(e => e.CreatedAt)
.HasDefaultValueSql("(getdate())")
.HasComment("创建时间")
.HasColumnName("created_at");
entity.Property(e => e.UpdatedAt)
.HasDefaultValueSql("(getdate())")
.HasComment("更新时间")
.HasColumnName("updated_at");
entity.Property(e => e.DeletedAt)
.HasComment("删除时间(软删除)")
.HasColumnName("deleted_at");
entity.HasOne(d => d.EquityLevel)
.WithMany(p => p.Prizes)
.HasForeignKey(d => d.QyLevelId)
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("fk_equity_level_prizes_equity_levels");
});
modelBuilder.Entity<GoodsKingRank>(entity =>
{
entity.HasKey(e => e.Id).HasName("pk_goods_king_ranks");

View File

@ -4,7 +4,7 @@ using System.Collections.Generic;
namespace HoneyBox.Model.Entities;
/// <summary>
/// 权益等级
/// 权益等级配置
/// </summary>
public partial class EquityLevel
{
@ -14,7 +14,7 @@ public partial class EquityLevel
public int Id { get; set; }
/// <summary>
/// 等级
/// 权益等级
/// </summary>
public int Level { get; set; }
@ -24,27 +24,22 @@ public partial class EquityLevel
public string Title { get; set; } = null!;
/// <summary>
/// 所需欧气值
/// 升级所需欧气值
/// </summary>
public int RequiredPoints { get; set; }
public int Number { get; set; }
/// <summary>
/// 创建时间
/// </summary>
public DateTime? CreatedAt { get; set; }
public DateTime CreatedAt { get; set; }
/// <summary>
/// 更新时间
/// </summary>
public DateTime? UpdatedAt { get; set; }
public DateTime UpdatedAt { get; set; }
/// <summary>
/// 删除时间(软删除)
/// 删除时间
/// </summary>
public DateTime? DeletedAt { get; set; }
/// <summary>
/// 关联的奖品列表
/// </summary>
public virtual ICollection<EquityLevelPrize> Prizes { get; set; } = new List<EquityLevelPrize>();
}

View File

@ -1,119 +0,0 @@
using System;
namespace HoneyBox.Model.Entities;
/// <summary>
/// 权益等级奖品表
/// </summary>
public partial class EquityLevelPrize
{
/// <summary>
/// 主键ID
/// </summary>
public int Id { get; set; }
/// <summary>
/// 权益等级ID
/// </summary>
public int QyLevelId { get; set; }
/// <summary>
/// 权益等级
/// </summary>
public int QyLevel { get; set; }
/// <summary>
/// 奖品类型1-优惠券 2-实物奖品
/// </summary>
public byte Type { get; set; }
/// <summary>
/// 奖品标题
/// </summary>
public string? Title { get; set; }
/// <summary>
/// 优惠券ID
/// </summary>
public int? CouponId { get; set; }
/// <summary>
/// 优惠券数量
/// </summary>
public int? ZNum { get; set; }
/// <summary>
/// 满减门槛
/// </summary>
public decimal? ManPrice { get; set; }
/// <summary>
/// 减免金额
/// </summary>
public decimal? JianPrice { get; set; }
/// <summary>
/// 有效天数
/// </summary>
public int? EffectiveDay { get; set; }
/// <summary>
/// 奖品价值
/// </summary>
public decimal? JiangPrice { get; set; }
/// <summary>
/// 兑换价格
/// </summary>
public decimal? Money { get; set; }
/// <summary>
/// 市场参考价
/// </summary>
public decimal? ScMoney { get; set; }
/// <summary>
/// 中奖概率0-100
/// </summary>
public decimal? Probability { get; set; }
/// <summary>
/// 奖品图片URL
/// </summary>
public string? ImgUrl { get; set; }
/// <summary>
/// 奖品编码
/// </summary>
public string? PrizeCode { get; set; }
/// <summary>
/// 奖品等级ID
/// </summary>
public int? ShangId { get; set; }
/// <summary>
/// 排序
/// </summary>
public int? Sort { get; set; }
/// <summary>
/// 创建时间
/// </summary>
public DateTime? CreatedAt { get; set; }
/// <summary>
/// 更新时间
/// </summary>
public DateTime? UpdatedAt { get; set; }
/// <summary>
/// 删除时间(软删除)
/// </summary>
public DateTime? DeletedAt { get; set; }
/// <summary>
/// 关联的权益等级
/// </summary>
public virtual EquityLevel? EquityLevel { get; set; }
}

View File

@ -1,12 +1,12 @@
using System;
using System;
using System.Collections.Generic;
namespace HoneyBox.Model.Entities;
/// <summary>
/// VIP等级奖品配置表
/// 权益等级奖品配置表
/// </summary>
public partial class VipLevelReward
public partial class EquityLevelReward
{
/// <summary>
/// 主键ID
@ -14,14 +14,14 @@ public partial class VipLevelReward
public int Id { get; set; }
/// <summary>
/// VIP等级ID
/// 权益等级ID
/// </summary>
public int VipLevelId { get; set; }
public int EquityLevelId { get; set; }
/// <summary>
/// VIP等级
/// 权益等级
/// </summary>
public int? VipLevel { get; set; }
public int? EquityLevel { get; set; }
/// <summary>
/// 奖品类型

View File

@ -1,12 +1,12 @@
using System;
using System;
using System.Collections.Generic;
namespace HoneyBox.Model.Entities;
/// <summary>
/// 用户领取的VIP奖品表
/// 用户领取的权益奖品表
/// </summary>
public partial class UserVipReward
public partial class UserEquityReward
{
/// <summary>
/// 主键ID
@ -19,14 +19,14 @@ public partial class UserVipReward
public int? UserId { get; set; }
/// <summary>
/// VIP等级ID
/// 权益等级ID
/// </summary>
public int VipLevelId { get; set; }
public int EquityLevelId { get; set; }
/// <summary>
/// VIP等级
/// 权益等级
/// </summary>
public int? VipLevel { get; set; }
public int? EquityLevel { get; set; }
/// <summary>
/// 优惠券ID

View File

@ -1,45 +0,0 @@
using System;
using System.Collections.Generic;
namespace HoneyBox.Model.Entities;
/// <summary>
/// VIP等级配置表
/// </summary>
public partial class VipLevel
{
/// <summary>
/// 主键ID
/// </summary>
public int Id { get; set; }
/// <summary>
/// VIP等级
/// </summary>
public int Level { get; set; }
/// <summary>
/// 等级名称
/// </summary>
public string Title { get; set; } = null!;
/// <summary>
/// 升级所需数量
/// </summary>
public int Number { get; set; }
/// <summary>
/// 创建时间
/// </summary>
public DateTime CreatedAt { get; set; }
/// <summary>
/// 更新时间
/// </summary>
public DateTime UpdatedAt { get; set; }
/// <summary>
/// 删除时间
/// </summary>
public DateTime? DeletedAt { get; set; }
}

View File

@ -1,29 +1,29 @@
using System.Text.Json.Serialization;
namespace HoneyBox.Model.Models.Vip;
namespace HoneyBox.Model.Models.Equity;
/// <summary>
/// VIP信息响应
/// 权益等级信息响应
/// </summary>
public class VipInfoResponse
public class EquityInfoResponse
{
/// <summary>
/// 用户VIP信息
/// 用户权益信息
/// </summary>
[JsonPropertyName("userinfo")]
public VipUserInfoDto Userinfo { get; set; } = new();
public EquityUserInfoDto Userinfo { get; set; } = new();
/// <summary>
/// VIP等级列表
/// 权益等级列表
/// </summary>
[JsonPropertyName("data")]
public List<VipLevelDto> Data { get; set; } = new();
public List<EquityLevelDto> Data { get; set; } = new();
}
/// <summary>
/// VIP用户信息DTO
/// 权益用户信息DTO
/// </summary>
public class VipUserInfoDto
public class EquityUserInfoDto
{
/// <summary>
/// 昵称
@ -38,7 +38,7 @@ public class VipUserInfoDto
public string Headimg { get; set; } = string.Empty;
/// <summary>
/// 当前VIP等级
/// 当前权益等级
/// </summary>
[JsonPropertyName("vip")]
public int Vip { get; set; }
@ -50,7 +50,7 @@ public class VipUserInfoDto
public string UpgradeMoney { get; set; } = string.Empty;
/// <summary>
/// 下一个VIP等级
/// 下一个权益等级
/// </summary>
[JsonPropertyName("last_vip")]
public int LastVip { get; set; }
@ -69,9 +69,9 @@ public class VipUserInfoDto
}
/// <summary>
/// VIP等级DTO
/// 权益等级DTO
/// </summary>
public class VipLevelDto
public class EquityLevelDto
{
/// <summary>
/// 等级ID

View File

@ -1,6 +1,6 @@
using System.Text.Json.Serialization;
namespace HoneyBox.Model.Models.Vip;
namespace HoneyBox.Model.Models.Equity;
/// <summary>
/// 权益中心响应

View File

@ -182,7 +182,7 @@ public class QyLevelServiceTests : IDisposable
var level = await _dbContext.EquityLevels.FindAsync(1);
Assert.NotNull(level);
Assert.Equal("更新后的青铜会员", level.Title);
Assert.Equal(200, level.RequiredPoints);
Assert.Equal(200, level.Number);
}
[Fact]
@ -239,7 +239,7 @@ public class QyLevelServiceTests : IDisposable
}
[Fact]
public async Task UpdateQyLevelAsync_WithInvalidRequiredPoints_ThrowsException()
public async Task UpdateQyLevelAsync_WithNegativeRequiredPoints_ThrowsException()
{
// Arrange
await SeedTestQyLevels();
@ -247,7 +247,7 @@ public class QyLevelServiceTests : IDisposable
{
Level = 1,
Title = "测试",
RequiredPoints = 0
RequiredPoints = -1
};
// Act & Assert
@ -311,7 +311,7 @@ public class QyLevelServiceTests : IDisposable
// Arrange
await SeedTestQyLevelsWithPrizes();
// Mark one prize as deleted
var prize = await _dbContext.EquityLevelPrizes.FindAsync(1);
var prize = await _dbContext.EquityLevelRewards.FindAsync(1);
prize!.DeletedAt = DateTime.Now;
await _dbContext.SaveChangesAsync();
@ -351,7 +351,7 @@ public class QyLevelServiceTests : IDisposable
// Assert
Assert.True(id > 0);
var prize = await _dbContext.EquityLevelPrizes.FindAsync(id);
var prize = await _dbContext.EquityLevelRewards.FindAsync(id);
Assert.NotNull(prize);
Assert.Equal(2, prize.Type);
Assert.Equal("测试实物奖品", prize.Title);
@ -374,7 +374,7 @@ public class QyLevelServiceTests : IDisposable
// Assert
Assert.True(id > 0);
var prize = await _dbContext.EquityLevelPrizes.FindAsync(id);
var prize = await _dbContext.EquityLevelRewards.FindAsync(id);
Assert.NotNull(prize);
Assert.Equal(1, prize.Type);
Assert.Equal(1, prize.CouponId);
@ -523,7 +523,7 @@ public class QyLevelServiceTests : IDisposable
// Assert
Assert.True(result);
var prize = await _dbContext.EquityLevelPrizes.FindAsync(2);
var prize = await _dbContext.EquityLevelRewards.FindAsync(2);
Assert.NotNull(prize);
Assert.Equal("更新后的奖品", prize.Title);
Assert.Equal(200, prize.JiangPrice);
@ -564,7 +564,7 @@ public class QyLevelServiceTests : IDisposable
// Assert
Assert.True(result);
var prize = await _dbContext.EquityLevelPrizes.FindAsync(1);
var prize = await _dbContext.EquityLevelRewards.FindAsync(1);
Assert.NotNull(prize);
Assert.NotNull(prize.DeletedAt);
}
@ -591,7 +591,7 @@ public class QyLevelServiceTests : IDisposable
Id = 1,
Level = 1,
Title = "青铜会员",
RequiredPoints = 100,
Number = 100,
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now
},
@ -600,7 +600,7 @@ public class QyLevelServiceTests : IDisposable
Id = 2,
Level = 2,
Title = "白银会员",
RequiredPoints = 500,
Number = 500,
CreatedAt = DateTime.Now.AddMinutes(-1),
UpdatedAt = DateTime.Now.AddMinutes(-1)
},
@ -609,7 +609,7 @@ public class QyLevelServiceTests : IDisposable
Id = 3,
Level = 3,
Title = "黄金会员",
RequiredPoints = 1000,
Number = 1000,
CreatedAt = DateTime.Now.AddMinutes(-2),
UpdatedAt = DateTime.Now.AddMinutes(-2)
}
@ -623,26 +623,23 @@ public class QyLevelServiceTests : IDisposable
{
await SeedTestQyLevels();
var prizes = new List<EquityLevelPrize>
var prizes = new List<EquityLevelReward>
{
new()
{
Id = 1,
QyLevelId = 1,
QyLevel = 1,
EquityLevelId = 1,
Type = 1,
Title = "优惠券奖品",
CouponId = 1,
ZNum = 5,
Sort = 1,
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now
},
new()
{
Id = 2,
QyLevelId = 1,
QyLevel = 1,
EquityLevelId = 1,
Type = 2,
Title = "实物奖品",
JiangPrice = 100,
@ -650,13 +647,12 @@ public class QyLevelServiceTests : IDisposable
ScMoney = 120,
Probability = 10.5m,
ImgUrl = "http://example.com/image.jpg",
Sort = 2,
CreatedAt = DateTime.Now.AddMinutes(-1),
UpdatedAt = DateTime.Now.AddMinutes(-1)
}
};
_dbContext.EquityLevelPrizes.AddRange(prizes);
_dbContext.EquityLevelRewards.AddRange(prizes);
await _dbContext.SaveChangesAsync();
}

View File

@ -124,21 +124,6 @@ public class UserBusinessServiceTests : IDisposable
return (goods, item);
}
private async Task<VipLevel> CreateTestVipLevelAsync()
{
var vipLevel = new VipLevel
{
Level = 1,
Title = "VIP1",
Number = 100,
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now
};
_dbContext.VipLevels.Add(vipLevel);
await _dbContext.SaveChangesAsync();
return vipLevel;
}
#endregion
#region GetUserListAsync Tests
@ -627,68 +612,6 @@ public class UserBusinessServiceTests : IDisposable
#endregion
#region VIP Management Tests
[Fact]
public async Task GetVipLevelsAsync_ReturnsAllLevels()
{
// Arrange
await CreateTestVipLevelAsync();
var vip2 = new VipLevel
{
Level = 2,
Title = "VIP2",
Number = 500,
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now
};
_dbContext.VipLevels.Add(vip2);
await _dbContext.SaveChangesAsync();
// Act
var result = await _userService.GetVipLevelsAsync();
// Assert
Assert.Equal(2, result.Count);
Assert.Equal("VIP1", result[0].Title);
Assert.Equal("VIP2", result[1].Title);
}
[Fact]
public async Task UpdateVipLevelAsync_UpdatesLevel()
{
// Arrange
var vipLevel = await CreateTestVipLevelAsync();
var request = new VipLevelUpdateRequest
{
Title = "Updated VIP",
Number = 200
};
// Act
var result = await _userService.UpdateVipLevelAsync(vipLevel.Id, request);
// Assert
Assert.True(result);
var updated = await _dbContext.VipLevels.FindAsync(vipLevel.Id);
Assert.Equal("Updated VIP", updated!.Title);
Assert.Equal(200, updated.Number);
}
[Fact]
public async Task UpdateVipLevelAsync_NonExistingLevel_ThrowsException()
{
// Arrange
var request = new VipLevelUpdateRequest { Title = "Test", Number = 100 };
// Act & Assert
var ex = await Assert.ThrowsAsync<BusinessException>(
() => _userService.UpdateVipLevelAsync(99999, request));
Assert.Equal(BusinessErrorCodes.NotFound, ex.Code);
}
#endregion
#region GetSubordinateUsersAsync Tests
[Fact]

View File

@ -141,7 +141,7 @@ public class UserServicePropertyTests
/// <summary>
/// Property 11: VIP等级计算
/// For any user with a given spending amount, CalculateVipLevelAsync should return
/// a VIP level that matches the spending threshold configured in VipLevel table.
/// a VIP level that matches the spending threshold configured in EquityLevel table.
/// Validates: Requirements 4.5
/// </summary>
[Property(MaxTest = 100)]
@ -150,15 +150,15 @@ public class UserServicePropertyTests
var dbContext = CreateInMemoryDbContext();
var userService = CreateUserService(dbContext);
// Setup VIP levels
var vipLevels = new[]
// Setup equity levels
var equityLevels = new[]
{
new VipLevel { Level = 1, Title = "Bronze", Number = 100, CreatedAt = DateTime.UtcNow, UpdatedAt = DateTime.UtcNow },
new VipLevel { Level = 2, Title = "Silver", Number = 500, CreatedAt = DateTime.UtcNow, UpdatedAt = DateTime.UtcNow },
new VipLevel { Level = 3, Title = "Gold", Number = 1000, CreatedAt = DateTime.UtcNow, UpdatedAt = DateTime.UtcNow }
new EquityLevel { Level = 1, Title = "Bronze", Number = 100, CreatedAt = DateTime.UtcNow, UpdatedAt = DateTime.UtcNow },
new EquityLevel { Level = 2, Title = "Silver", Number = 500, CreatedAt = DateTime.UtcNow, UpdatedAt = DateTime.UtcNow },
new EquityLevel { Level = 3, Title = "Gold", Number = 1000, CreatedAt = DateTime.UtcNow, UpdatedAt = DateTime.UtcNow }
};
await dbContext.VipLevels.AddRangeAsync(vipLevels);
await dbContext.EquityLevels.AddRangeAsync(equityLevels);
await dbContext.SaveChangesAsync();
// Create user with spending