339 lines
11 KiB
Markdown
339 lines
11 KiB
Markdown
# Design Document
|
||
|
||
## Overview
|
||
|
||
本设计文档描述后台管理系统 9 个未修复 BUG 的技术修复方案。BUG 分布在统计报表、商品管理、营销活动和内容管理四个模块,主要涉及后端接口修复和少量前端调整。
|
||
|
||
## Architecture
|
||
|
||
### 受影响模块架构
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────┐
|
||
│ 前端 (Vue 3 + Element Plus) │
|
||
├─────────────────────────────────────────────────────────────────┤
|
||
│ views/business/ │
|
||
│ ├── statistics/ # 统计报表 (cs100_2, cs100_3) │
|
||
│ ├── goods/ # 商品管理 (cs100_4, cs100_5, cs100_6) │
|
||
│ ├── marketing/ # 营销活动 (cs100_11, cs100_13, cs100_14) │
|
||
│ └── danye/ # 内容管理 (cs100_15) │
|
||
└─────────────────────────────────────────────────────────────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────────┐
|
||
│ 后端 (ASP.NET Core) │
|
||
├─────────────────────────────────────────────────────────────────┤
|
||
│ HoneyBox.Admin.Business/ │
|
||
│ ├── Controllers/ │
|
||
│ │ ├── StatisticsController.cs # 统计报表接口 │
|
||
│ │ ├── GoodsController.cs # 商品管理接口 │
|
||
│ │ ├── MarketingController.cs # 营销活动接口 │
|
||
│ │ └── DanyeController.cs # 单页管理接口 │
|
||
│ └── Services/ │
|
||
│ ├── StatisticsService.cs │
|
||
│ ├── GoodsService.cs │
|
||
│ ├── MarketingService.cs │
|
||
│ └── DanyeService.cs │
|
||
└─────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
## Bug Analysis & Solutions
|
||
|
||
### 1. 统计报表模块 (cs100_2, cs100_3)
|
||
|
||
#### 问题分析
|
||
- 用户盈亏统计和用户邀请统计按用户ID搜索时返回"请求失败"
|
||
- 可能原因:
|
||
1. 用户ID参数类型不匹配(前端传string,后端期望int)
|
||
2. 查询条件构建错误
|
||
3. 空值处理异常
|
||
|
||
#### 修复方案
|
||
```csharp
|
||
// StatisticsService.cs
|
||
public async Task<PagedResult<UserProfitStatsResponse>> GetUserProfitStatsAsync(
|
||
UserProfitStatsRequest request)
|
||
{
|
||
var query = _dbContext.UserProfitStats.AsQueryable();
|
||
|
||
// 修复:正确处理用户ID搜索
|
||
if (!string.IsNullOrEmpty(request.UserId))
|
||
{
|
||
if (int.TryParse(request.UserId, out var userId))
|
||
{
|
||
query = query.Where(x => x.UserId == userId);
|
||
}
|
||
}
|
||
|
||
// ... 分页逻辑
|
||
}
|
||
```
|
||
|
||
### 2. 商品管理模块 - 盒子类型搜索 (cs100_4)
|
||
|
||
#### 问题分析
|
||
- 盒子类型为"全部"时搜索无结果
|
||
- 可能原因:
|
||
1. "全部"选项传递了错误的值(如0)被当作有效类型过滤
|
||
2. 后端未正确处理"全部"情况
|
||
|
||
#### 修复方案
|
||
```csharp
|
||
// GoodsService.cs
|
||
public async Task<PagedResult<BoxResponse>> GetBoxListAsync(BoxListRequest request)
|
||
{
|
||
var query = _dbContext.Boxes.AsQueryable();
|
||
|
||
// 修复:当类型为0或null时,不添加类型过滤条件
|
||
if (request.Type.HasValue && request.Type.Value > 0)
|
||
{
|
||
query = query.Where(x => x.Type == request.Type.Value);
|
||
}
|
||
|
||
// ... 其他过滤和分页逻辑
|
||
}
|
||
```
|
||
|
||
### 3. 商品管理模块 - 自动下架配置 (cs100_5)
|
||
|
||
#### 问题分析
|
||
- 开启"自动下架配置"后无法设置参数
|
||
- 可能原因:
|
||
1. 前端表单联动逻辑问题
|
||
2. 后端未返回自动下架相关字段
|
||
3. 保存接口未处理这些字段
|
||
|
||
#### 修复方案
|
||
|
||
**前端修复:**
|
||
```vue
|
||
<!-- GoodsFormDialog.vue -->
|
||
<template>
|
||
<el-form-item label="自动下架配置">
|
||
<el-switch v-model="form.autoDelistEnabled" />
|
||
</el-form-item>
|
||
|
||
<!-- 修复:确保联动显示 -->
|
||
<template v-if="form.autoDelistEnabled">
|
||
<el-form-item label="下架时间">
|
||
<el-date-picker v-model="form.autoDelistTime" type="datetime" />
|
||
</el-form-item>
|
||
<el-form-item label="下架条件">
|
||
<el-input v-model="form.autoDelistCondition" />
|
||
</el-form-item>
|
||
</template>
|
||
</template>
|
||
```
|
||
|
||
**后端修复:**
|
||
```csharp
|
||
// BoxResponse.cs - 确保返回自动下架字段
|
||
public class BoxResponse
|
||
{
|
||
// ... 其他字段
|
||
public bool AutoDelistEnabled { get; set; }
|
||
public DateTime? AutoDelistTime { get; set; }
|
||
public string? AutoDelistCondition { get; set; }
|
||
}
|
||
```
|
||
|
||
### 4. 商品管理模块 - 奖品等级分类显示 (cs100_6)
|
||
|
||
#### 问题分析
|
||
- 奖品等级显示"0级",分类显示"未知"
|
||
- 可能原因:
|
||
1. 未进行关联查询获取等级和分类名称
|
||
2. DTO映射时字段未正确赋值
|
||
|
||
#### 修复方案
|
||
```csharp
|
||
// GoodsService.cs
|
||
public async Task<PagedResult<PrizeResponse>> GetPrizeListAsync(PrizeListRequest request)
|
||
{
|
||
var query = from p in _dbContext.Prizes
|
||
join l in _dbContext.PrizeLevels on p.LevelId equals l.Id into levels
|
||
from level in levels.DefaultIfEmpty()
|
||
join c in _dbContext.PrizeCategories on p.CategoryId equals c.Id into categories
|
||
from category in categories.DefaultIfEmpty()
|
||
select new PrizeResponse
|
||
{
|
||
Id = p.Id,
|
||
Name = p.Name,
|
||
LevelId = p.LevelId,
|
||
LevelName = level != null ? level.Name : "未设置", // 修复
|
||
CategoryId = p.CategoryId,
|
||
CategoryName = category != null ? category.Name : "未设置", // 修复
|
||
// ... 其他字段
|
||
};
|
||
|
||
// ... 分页逻辑
|
||
}
|
||
```
|
||
|
||
### 5. 营销活动模块 - 领取记录搜索 (cs100_11)
|
||
|
||
#### 问题分析
|
||
- 按用户ID搜索领取记录时返回"请求失败"
|
||
- 与统计报表模块问题类似
|
||
|
||
#### 修复方案
|
||
```csharp
|
||
// MarketingService.cs
|
||
public async Task<PagedResult<ClaimRecordResponse>> GetClaimRecordsAsync(
|
||
ClaimRecordRequest request)
|
||
{
|
||
var query = _dbContext.ClaimRecords.AsQueryable();
|
||
|
||
// 修复:正确处理用户ID搜索
|
||
if (!string.IsNullOrEmpty(request.UserId))
|
||
{
|
||
if (int.TryParse(request.UserId, out var userId))
|
||
{
|
||
query = query.Where(x => x.UserId == userId);
|
||
}
|
||
}
|
||
|
||
// ... 分页逻辑
|
||
}
|
||
```
|
||
|
||
### 6. 营销活动模块 - 周榜/月榜记录 (cs100_13, cs100_14)
|
||
|
||
#### 问题分析
|
||
- 访问周榜/月榜页面时返回"服务器内部错误"
|
||
- 可能原因:
|
||
1. 日期范围计算错误
|
||
2. 数据聚合查询异常
|
||
3. 空数据处理异常
|
||
|
||
#### 修复方案
|
||
```csharp
|
||
// MarketingService.cs
|
||
public async Task<List<RankRecordResponse>> GetWeeklyRankAsync(RankRequest request)
|
||
{
|
||
try
|
||
{
|
||
// 修复:正确计算周起止日期
|
||
var startOfWeek = GetStartOfWeek(request.Date ?? DateTime.Now);
|
||
var endOfWeek = startOfWeek.AddDays(7);
|
||
|
||
var query = _dbContext.RankRecords
|
||
.Where(x => x.RecordDate >= startOfWeek && x.RecordDate < endOfWeek)
|
||
.OrderByDescending(x => x.Score);
|
||
|
||
// 修复:处理空数据情况
|
||
var result = await query.ToListAsync();
|
||
return result ?? new List<RankRecordResponse>();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError(ex, "获取周榜数据失败");
|
||
throw new BusinessException("获取周榜数据失败,请稍后重试");
|
||
}
|
||
}
|
||
|
||
private DateTime GetStartOfWeek(DateTime date)
|
||
{
|
||
var diff = (7 + (date.DayOfWeek - DayOfWeek.Monday)) % 7;
|
||
return date.AddDays(-diff).Date;
|
||
}
|
||
```
|
||
|
||
### 7. 内容管理模块 - 单页文章显示 (cs100_15)
|
||
|
||
#### 问题分析
|
||
- 文章内容显示原始HTML代码而非渲染内容
|
||
- 原因:前端使用 `{{ content }}` 而非 `v-html`
|
||
|
||
#### 修复方案
|
||
```vue
|
||
<!-- DanyeFormDialog.vue 或详情组件 -->
|
||
<template>
|
||
<!-- 修复前 -->
|
||
<!-- <div>{{ article.content }}</div> -->
|
||
|
||
<!-- 修复后:使用 v-html 渲染 -->
|
||
<div class="article-content" v-html="sanitizedContent"></div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { computed } from 'vue'
|
||
import DOMPurify from 'dompurify'
|
||
|
||
const props = defineProps<{
|
||
article: { content: string }
|
||
}>()
|
||
|
||
// 安全处理HTML内容
|
||
const sanitizedContent = computed(() => {
|
||
return DOMPurify.sanitize(props.article.content)
|
||
})
|
||
</script>
|
||
|
||
<style scoped>
|
||
.article-content {
|
||
/* 确保样式正确渲染 */
|
||
line-height: 1.6;
|
||
}
|
||
.article-content img {
|
||
max-width: 100%;
|
||
}
|
||
</style>
|
||
```
|
||
|
||
## API Changes
|
||
|
||
### 统计报表 API 修复
|
||
|
||
| 方法 | 路径 | 修复内容 |
|
||
|------|------|----------|
|
||
| GET | /api/business/statistics/user-profit | 修复用户ID参数处理 |
|
||
| GET | /api/business/statistics/user-invite | 修复用户ID参数处理 |
|
||
|
||
### 商品管理 API 修复
|
||
|
||
| 方法 | 路径 | 修复内容 |
|
||
|------|------|----------|
|
||
| GET | /api/business/goods/boxes | 修复类型"全部"过滤逻辑 |
|
||
| GET | /api/business/goods/boxes/{id} | 添加自动下架字段返回 |
|
||
| PUT | /api/business/goods/boxes/{id} | 处理自动下架字段保存 |
|
||
| GET | /api/business/goods/prizes | 修复等级/分类关联查询 |
|
||
|
||
### 营销活动 API 修复
|
||
|
||
| 方法 | 路径 | 修复内容 |
|
||
|------|------|----------|
|
||
| GET | /api/business/marketing/claim-records | 修复用户ID参数处理 |
|
||
| GET | /api/business/marketing/weekly-rank | 修复日期计算和异常处理 |
|
||
| GET | /api/business/marketing/monthly-rank | 修复日期计算和异常处理 |
|
||
|
||
## Testing Strategy
|
||
|
||
### 单元测试
|
||
- 测试用户ID参数解析逻辑
|
||
- 测试类型过滤条件构建
|
||
- 测试日期范围计算
|
||
- 测试关联查询结果映射
|
||
|
||
### 集成测试
|
||
- 测试各搜索接口正常返回
|
||
- 测试空数据情况处理
|
||
- 测试边界条件(无效ID、特殊日期等)
|
||
|
||
### 前端测试
|
||
- 测试表单联动显示
|
||
- 测试HTML内容渲染
|
||
- 测试XSS防护
|
||
|
||
## Security Considerations
|
||
|
||
1. **XSS防护**: 单页文章内容使用 DOMPurify 进行净化
|
||
2. **参数验证**: 所有搜索参数进行类型验证
|
||
3. **异常处理**: 统一异常处理,不暴露内部错误信息
|
||
|
||
## References
|
||
|
||
- BUG汇总清单: `docs/后台管理bug整理/BUG汇总清单.md`
|
||
- 各BUG详细文档: `docs/后台管理bug整理/BUG-*.md`
|
||
|