feat: 向用户显示管理员调整余额的备注

- 为RedeemCode DTO添加notes字段(仅用于admin_balance/admin_concurrency类型)
- 更新mapper使其有条件地包含备注信息
- 在用户兑换历史UI中显示备注
- 备注以斜体显示,悬停时显示完整内容

用户现在可以看到管理员调整其余额的原因说明。

Changes:
- backend/internal/handler/dto/types.go: RedeemCode添加notes字段
- backend/internal/handler/dto/mappers.go: 条件性填充notes
- frontend/src/api/redeem.ts: TypeScript接口添加notes
- frontend/src/views/user/RedeemView.vue: UI显示备注信息
This commit is contained in:
小北
2026-02-02 17:44:50 +08:00
parent c3d1891ccd
commit ae18397ca6
4 changed files with 24 additions and 2 deletions

View File

@@ -321,7 +321,7 @@ func RedeemCodeFromServiceAdmin(rc *service.RedeemCode) *AdminRedeemCode {
}
func redeemCodeFromServiceBase(rc *service.RedeemCode) RedeemCode {
return RedeemCode{
out := RedeemCode{
ID: rc.ID,
Code: rc.Code,
Type: rc.Type,
@@ -335,6 +335,14 @@ func redeemCodeFromServiceBase(rc *service.RedeemCode) RedeemCode {
User: UserFromServiceShallow(rc.User),
Group: GroupFromServiceShallow(rc.Group),
}
// For admin_balance/admin_concurrency types, include notes so users can see
// why they were charged or credited by admin
if (rc.Type == "admin_balance" || rc.Type == "admin_concurrency") && rc.Notes != "" {
out.Notes = &rc.Notes
}
return out
}
// AccountSummaryFromService returns a minimal AccountSummary for usage log display.

View File

@@ -198,6 +198,10 @@ type RedeemCode struct {
GroupID *int64 `json:"group_id"`
ValidityDays int `json:"validity_days"`
// Notes is only populated for admin_balance/admin_concurrency types
// so users can see why they were charged or credited
Notes *string `json:"notes,omitempty"`
User *User `json:"user,omitempty"`
Group *Group `json:"group,omitempty"`
}

View File

@@ -14,7 +14,9 @@ export interface RedeemHistoryItem {
status: string
used_at: string
created_at: string
// 订阅类型专用字段
// Notes from admin for admin_balance/admin_concurrency types
notes?: string
// Subscription-specific fields
group_id?: number
validity_days?: number
group?: {

View File

@@ -312,6 +312,14 @@
<p v-else class="text-xs text-gray-400 dark:text-dark-500">
{{ t('redeem.adminAdjustment') }}
</p>
<!-- Display notes for admin adjustments -->
<p
v-if="item.notes"
class="mt-1 text-xs text-gray-500 dark:text-dark-400 italic max-w-[200px] truncate"
:title="item.notes"
>
{{ item.notes }}
</p>
</div>
</div>
</div>