723 lines
22 KiB
Go
723 lines
22 KiB
Go
package mysql
|
||
|
||
import (
|
||
"acquaintances/biz/model"
|
||
"acquaintances/biz/model/user_assets"
|
||
"errors"
|
||
"fmt"
|
||
"github.com/shopspring/decimal"
|
||
"gorm.io/gorm"
|
||
"regexp"
|
||
"time"
|
||
)
|
||
|
||
func GetUserAssets(req user_assets.GetUserAssetsRequest) (*user_assets.UserAssets, error) { // 1. 参数验证 if req.UserID == "" { return nil, errors.New("用户ID不能为空") }
|
||
// 2. 初始化响应结构体
|
||
userAssets := &user_assets.UserAssets{
|
||
UserID: req.UserID, // 确保用户 ID 始终返回
|
||
}
|
||
// 3. 查询用户资产主表信息(状态、时间戳)
|
||
var mainAsset model.UserAssets
|
||
err := DB.Model(&model.UserAssets{}).
|
||
Where("user_id = ?", req.UserID).
|
||
First(&mainAsset).Error
|
||
// 4. 处理主表记录不存在的情况(不直接报错,允许资产明细单独存在)
|
||
if err != nil {
|
||
if !errors.Is(err, gorm.ErrRecordNotFound) {
|
||
// 非 "记录不存在" 的数据库错误才返回错误
|
||
return nil, fmt.Errorf("查询用户资产主表失败: % w", err)
|
||
}
|
||
|
||
}
|
||
// 5. 分别查询各类资产明细(避免 JOIN 导致的记录缺失问题)
|
||
var (
|
||
diamond model.Diamond
|
||
goldBean model.GoldBean
|
||
goldLeaf model.GoldLeaf
|
||
)
|
||
// 查询钻石
|
||
if err := DB.Where("user_id = ?", req.UserID).First(&diamond).Error; err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||
return nil, fmt.Errorf("查询钻石资产失败: %w", err)
|
||
}
|
||
userAssets.Diamond = diamond.Diamond.String()
|
||
// 查询金豆
|
||
if err := DB.Where("user_id = ?", req.UserID).First(&goldBean).Error; err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||
return nil, fmt.Errorf("查询金豆资产失败: %w", err)
|
||
}
|
||
userAssets.GoldBean = goldBean.GoldBean.String()
|
||
// 查询金叶
|
||
if err := DB.Where("user_id = ?", req.UserID).First(&goldLeaf).Error; err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||
return nil, fmt.Errorf("查询金叶资产失败: %w", err)
|
||
}
|
||
userAssets.GoldLeaf = goldLeaf.GoldLeaf.String()
|
||
// 6. 检查是否所有资产都不存在(可选:根据业务需求决定是否返回错误)
|
||
if mainAsset.ID == 0 && diamond.ID == 0 && goldBean.ID == 0 && goldLeaf.ID == 0 {
|
||
return nil, errors.New("用户资产记录不存在")
|
||
}
|
||
return userAssets, nil
|
||
}
|
||
|
||
// AddDiamond 为用户添加钻石(包含事务处理)
|
||
func AddDiamond(req user_assets.AddDiamondRequest) error {
|
||
// 1. 参数验证
|
||
if err := validateAddDiamondRequest(req); err != nil {
|
||
return fmt.Errorf("参数验证失败: %w", err)
|
||
}
|
||
|
||
// 2. 转换钻石数量为decimal类型
|
||
diamondNum, err := decimal.NewFromString(req.DiamondNumber)
|
||
if err != nil {
|
||
return fmt.Errorf("钻石数量格式错误: %w", err)
|
||
}
|
||
if diamondNum.LessThanOrEqual(decimal.Zero) {
|
||
return errors.New("钻石数量必须大于0")
|
||
}
|
||
|
||
// 查询银行卡信息,验证存在性和归属关系
|
||
var bankcard model.Bankcard
|
||
if err := DB.Where("bankcard_number = ?", req.BankcardNumber).First(&bankcard).Error; err != nil {
|
||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||
return errors.New("银行卡不存在")
|
||
}
|
||
return fmt.Errorf("查询银行卡信息失败: %w", err)
|
||
}
|
||
|
||
// 4. 验证用户ID(如果指定)
|
||
if bankcard.UserID != req.UserID {
|
||
return errors.New("银行卡不属于指定用户")
|
||
}
|
||
|
||
// 3. 使用事务确保数据一致性
|
||
return DB.Transaction(func(tx *gorm.DB) error {
|
||
// 4. 查询用户当前钻石数量
|
||
var diamond model.Diamond
|
||
result := tx.Where("user_id = ?", req.UserID).First(&diamond)
|
||
|
||
// 5. 处理用户钻石记录不存在的情况(自动初始化)
|
||
if result.Error != nil {
|
||
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
|
||
diamond = model.Diamond{
|
||
UserID: req.UserID,
|
||
Diamond: decimal.Zero,
|
||
}
|
||
} else {
|
||
return fmt.Errorf("查询用户钻石记录失败: %w", result.Error)
|
||
}
|
||
}
|
||
|
||
// 6. 计算更新后的值
|
||
beforeAmount := diamond.Diamond
|
||
afterAmount := beforeAmount.Add(diamondNum)
|
||
|
||
// 7. 更新钻石数量
|
||
if diamond.ID == 0 {
|
||
// 新增记录
|
||
diamond.Diamond = afterAmount
|
||
if err := tx.Create(&diamond).Error; err != nil {
|
||
return fmt.Errorf("创建钻石记录失败: %w", err)
|
||
}
|
||
} else {
|
||
// 更新现有记录
|
||
if err := tx.Model(&diamond).Update("diamond", afterAmount).Error; err != nil {
|
||
return fmt.Errorf("更新钻石数量失败: %w", err)
|
||
}
|
||
}
|
||
|
||
// 8. 记录消费记录(作为增加记录)
|
||
record := model.ConsumptionRecord{
|
||
UserID: req.UserID,
|
||
ItemType: model.ItemTypeDiamond,
|
||
Amount: diamondNum, // 正数表示增加
|
||
BeforeAmount: beforeAmount,
|
||
AfterAmount: afterAmount,
|
||
BusinessType: "recharge_diamond", // 业务类型:钻石充值
|
||
BusinessID: generateBusinessID(), // 生成业务唯一ID
|
||
Remark: req.BankcardNumber,
|
||
Operator: "system",
|
||
SourceType: model.SourceTypeBankcard,
|
||
}
|
||
if err := tx.Create(&record).Error; err != nil {
|
||
return fmt.Errorf("记录消费日志失败: %w", err)
|
||
}
|
||
|
||
return nil
|
||
})
|
||
}
|
||
|
||
// 验证请求参数
|
||
func validateAddDiamondRequest(req user_assets.AddDiamondRequest) error {
|
||
if req.UserID == "" {
|
||
return errors.New("用户ID不能为空")
|
||
}
|
||
if req.BankcardNumber == "" {
|
||
return errors.New("银行卡号不能为空")
|
||
}
|
||
if req.DiamondNumber == "" {
|
||
return errors.New("钻石数量不能为空")
|
||
}
|
||
// 可添加银行卡号格式验证
|
||
return nil
|
||
}
|
||
|
||
// 生成业务唯一ID
|
||
func generateBusinessID() string {
|
||
return fmt.Sprintf("diamond_%d", time.Now().UnixNano())
|
||
}
|
||
|
||
// AddGoldBean 为用户添加金豆(包含事务处理)
|
||
func AddGoldBean(req user_assets.AddGoldBeanRequest) error {
|
||
// 1. 参数验证
|
||
if err := validateAddGoldBeanRequest(req); err != nil {
|
||
return fmt.Errorf("参数验证失败: %w", err)
|
||
}
|
||
|
||
// 2. 转换金豆数量为decimal类型
|
||
goldBeanNum, err := decimal.NewFromString(req.GoldBeanNumber)
|
||
if err != nil {
|
||
return fmt.Errorf("金豆数量格式错误: %w", err)
|
||
}
|
||
if goldBeanNum.LessThanOrEqual(decimal.Zero) {
|
||
return errors.New("金豆数量必须大于0")
|
||
}
|
||
|
||
// 3. 使用事务确保数据一致性
|
||
return DB.Transaction(func(tx *gorm.DB) error {
|
||
// 4. 查询用户当前金豆数量
|
||
var goldBean model.GoldBean
|
||
result := tx.Where("user_id = ?", req.UserID).First(&goldBean)
|
||
|
||
// 5. 处理用户金豆记录不存在的情况(自动初始化)
|
||
if result.Error != nil {
|
||
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
|
||
goldBean = model.GoldBean{
|
||
UserID: req.UserID,
|
||
GoldBean: decimal.Zero,
|
||
}
|
||
} else {
|
||
return fmt.Errorf("查询用户金豆记录失败: %w", result.Error)
|
||
}
|
||
}
|
||
|
||
// 6. 计算更新后的值
|
||
beforeAmount := goldBean.GoldBean
|
||
afterAmount := beforeAmount.Add(goldBeanNum)
|
||
|
||
// 7. 更新金豆数量
|
||
if goldBean.ID == 0 {
|
||
// 新增记录
|
||
goldBean.GoldBean = afterAmount
|
||
if err := tx.Create(&goldBean).Error; err != nil {
|
||
return fmt.Errorf("创建金豆记录失败: %w", err)
|
||
}
|
||
} else {
|
||
// 更新现有记录
|
||
if err := tx.Model(&goldBean).Update("gold_bean", afterAmount).Error; err != nil {
|
||
return fmt.Errorf("更新金豆数量失败: %w", err)
|
||
}
|
||
}
|
||
|
||
// 8. 记录消费记录(作为增加记录)
|
||
record := model.ConsumptionRecord{
|
||
UserID: req.UserID,
|
||
ItemType: model.ItemTypeGoldBean, // 物品类型为金豆
|
||
Amount: goldBeanNum, // 正数表示增加
|
||
BeforeAmount: beforeAmount,
|
||
AfterAmount: afterAmount,
|
||
BusinessType: "recharge_gold_bean", // 业务类型:金豆充值
|
||
BusinessID: generateGoldBeanBusinessID(),
|
||
Remark: req.BankcardNumber,
|
||
Operator: "system",
|
||
SourceType: model.SourceTypeBankcard,
|
||
}
|
||
if err := tx.Create(&record).Error; err != nil {
|
||
return fmt.Errorf("记录金豆消费日志失败: %w", err)
|
||
}
|
||
|
||
return nil
|
||
})
|
||
}
|
||
|
||
// 验证请求参数
|
||
func validateAddGoldBeanRequest(req user_assets.AddGoldBeanRequest) error {
|
||
if req.UserID == "" {
|
||
return errors.New("用户ID不能为空")
|
||
}
|
||
if req.BankcardNumber == "" {
|
||
return errors.New("银行卡号不能为空")
|
||
}
|
||
if req.GoldBeanNumber == "" {
|
||
return errors.New("金豆数量不能为空")
|
||
}
|
||
// 可添加银行卡号格式验证(如长度、校验位等)
|
||
return nil
|
||
}
|
||
|
||
// 生成金豆业务唯一ID
|
||
func generateGoldBeanBusinessID() string {
|
||
return fmt.Sprintf("gold_bean_%d", time.Now().UnixNano())
|
||
}
|
||
|
||
// AddGoldLeaf 为用户添加金叶(包含事务处理)
|
||
func AddGoldLeaf(req user_assets.AddGoldLeafRequest) error {
|
||
// 1. 参数验证
|
||
if err := validateAddGoldLeafRequest(req); err != nil {
|
||
return fmt.Errorf("参数验证失败: %w", err)
|
||
}
|
||
|
||
// 2. 转换金叶数量为decimal类型
|
||
goldLeafNum, err := decimal.NewFromString(req.GoldLeafNumber)
|
||
if err != nil {
|
||
return fmt.Errorf("金叶数量格式错误: %w", err)
|
||
}
|
||
if goldLeafNum.LessThanOrEqual(decimal.Zero) {
|
||
return errors.New("金叶数量必须大于0")
|
||
}
|
||
|
||
// 3. 使用事务确保数据一致性
|
||
return DB.Transaction(func(tx *gorm.DB) error {
|
||
// 4. 查询用户当前金叶数量
|
||
var goldLeaf model.GoldLeaf
|
||
result := tx.Where("user_id = ?", req.UserID).First(&goldLeaf)
|
||
|
||
// 5. 处理用户金叶记录不存在的情况(自动初始化)
|
||
if result.Error != nil {
|
||
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
|
||
goldLeaf = model.GoldLeaf{
|
||
UserID: req.UserID,
|
||
GoldLeaf: decimal.Zero,
|
||
}
|
||
} else {
|
||
return fmt.Errorf("查询用户金叶记录失败: %w", result.Error)
|
||
}
|
||
}
|
||
|
||
// 6. 计算更新后的值
|
||
beforeAmount := goldLeaf.GoldLeaf
|
||
afterAmount := beforeAmount.Add(goldLeafNum)
|
||
|
||
// 7. 更新金叶数量
|
||
if goldLeaf.ID == 0 {
|
||
// 新增记录
|
||
goldLeaf.GoldLeaf = afterAmount
|
||
if err := tx.Create(&goldLeaf).Error; err != nil {
|
||
return fmt.Errorf("创建金叶记录失败: %w", err)
|
||
}
|
||
} else {
|
||
// 更新现有记录
|
||
if err := tx.Model(&goldLeaf).Update("gold_leaf", afterAmount).Error; err != nil {
|
||
return fmt.Errorf("更新金叶数量失败: %w", err)
|
||
}
|
||
}
|
||
|
||
// 8. 记录消费记录(作为增加记录)
|
||
record := model.ConsumptionRecord{
|
||
UserID: req.UserID,
|
||
ItemType: model.ItemTypeGoldLeaf, // 物品类型为金叶
|
||
Amount: goldLeafNum, // 正数表示增加
|
||
BeforeAmount: beforeAmount,
|
||
AfterAmount: afterAmount,
|
||
BusinessType: "recharge_gold_leaf", // 业务类型:金叶充值
|
||
BusinessID: generateGoldLeafBusinessID(),
|
||
Remark: req.BankcardNumber,
|
||
Operator: "system",
|
||
SourceType: model.SourceTypeBankcard,
|
||
}
|
||
if err := tx.Create(&record).Error; err != nil {
|
||
return fmt.Errorf("记录金叶消费日志失败: %w", err)
|
||
}
|
||
|
||
return nil
|
||
})
|
||
}
|
||
|
||
// 验证请求参数
|
||
func validateAddGoldLeafRequest(req user_assets.AddGoldLeafRequest) error {
|
||
if req.UserID == "" {
|
||
return errors.New("用户ID不能为空")
|
||
}
|
||
if req.BankcardNumber == "" {
|
||
return errors.New("银行卡号不能为空")
|
||
}
|
||
if req.GoldLeafNumber == "" {
|
||
return errors.New("金叶数量不能为空")
|
||
}
|
||
// 可添加银行卡号格式验证(如16-19位数字校验)
|
||
return nil
|
||
}
|
||
|
||
// 生成金叶业务唯一ID
|
||
func generateGoldLeafBusinessID() string {
|
||
return fmt.Sprintf("gold_leaf_%d", time.Now().UnixNano())
|
||
}
|
||
|
||
// 银行卡号格式验证正则(支持16-19位数字)
|
||
var bankcardRegex = regexp.MustCompile(`^\d{16,19}$`)
|
||
|
||
// AddBankcard 为用户添加银行卡
|
||
func AddBankcard(req user_assets.AddBankcardRequest) error {
|
||
// 1. 参数验证
|
||
if err := validateAddBankcardRequest(req); err != nil {
|
||
return fmt.Errorf("参数验证失败: %w", err)
|
||
}
|
||
|
||
// 2. 使用事务处理,确保默认卡状态一致性
|
||
return DB.Transaction(func(tx *gorm.DB) error {
|
||
// 3. 检查银行卡号是否已被当前用户添加
|
||
var existingCount int64
|
||
if err := tx.Model(&model.Bankcard{}).
|
||
Where("user_id = ? AND bankcard_number = ?", req.UserID, req.BankcardNumber).
|
||
Count(&existingCount).Error; err != nil {
|
||
return fmt.Errorf("查询银行卡记录失败: %w", err)
|
||
}
|
||
if existingCount > 0 {
|
||
return errors.New("该银行卡已添加,不可重复添加")
|
||
}
|
||
|
||
// 4. 如果设置为默认卡,需要将用户其他默认卡设为非默认
|
||
if req.IsDefault {
|
||
if err := tx.Model(&model.Bankcard{}).
|
||
Where("user_id = ? AND is_default = 1", req.UserID).
|
||
Update("is_default", false).Error; err != nil {
|
||
return fmt.Errorf("更新默认银行卡状态失败: %w", err)
|
||
}
|
||
} else {
|
||
// 5. 如果用户没有默认卡,强制设置当前卡为默认卡
|
||
var defaultCount int64
|
||
if err := tx.Model(&model.Bankcard{}).
|
||
Where("user_id = ? AND is_default = 1", req.UserID).
|
||
Count(&defaultCount).Error; err != nil {
|
||
return fmt.Errorf("查询默认银行卡失败: %w", err)
|
||
}
|
||
if defaultCount == 0 {
|
||
req.IsDefault = true // 自动设为默认卡
|
||
}
|
||
}
|
||
|
||
// 6. 创建银行卡记录
|
||
bankcard := model.Bankcard{
|
||
UserID: req.UserID,
|
||
BankCardNumber: req.BankcardNumber,
|
||
BankName: req.BankName,
|
||
AccountType: req.AccountType,
|
||
IsDefault: req.IsDefault,
|
||
AccountName: req.AccountName,
|
||
}
|
||
if err := tx.Create(&bankcard).Error; err != nil {
|
||
return fmt.Errorf("创建银行卡记录失败: %w", err)
|
||
}
|
||
|
||
return nil
|
||
})
|
||
}
|
||
|
||
// 验证银行卡添加请求参数
|
||
func validateAddBankcardRequest(req user_assets.AddBankcardRequest) error {
|
||
if req.UserID == "" {
|
||
return errors.New("用户ID不能为空")
|
||
}
|
||
if req.BankcardNumber == "" {
|
||
return errors.New("银行卡号不能为空")
|
||
}
|
||
// 验证银行卡号格式(16-19位数字)
|
||
if !bankcardRegex.MatchString(req.BankcardNumber) {
|
||
return errors.New("银行卡号格式错误,应为16-19位数字")
|
||
}
|
||
if req.BankName == "" {
|
||
return errors.New("银行名称不能为空")
|
||
}
|
||
if req.AccountName == "" {
|
||
return errors.New("账户名(持卡人姓名)不能为空")
|
||
}
|
||
// 账户类型为空时设置默认值
|
||
if req.AccountType == "" {
|
||
req.AccountType = "basic"
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// RemoveBankcard 为用户删除银行卡
|
||
func RemoveBankcard(req user_assets.RemoveBankcardRequest) error {
|
||
// 1. 参数验证
|
||
if err := validateRemoveBankcardRequest(req); err != nil {
|
||
return fmt.Errorf("参数验证失败: %w", err)
|
||
}
|
||
|
||
// 2. 使用事务处理,确保删除后默认卡状态合理
|
||
return DB.Transaction(func(tx *gorm.DB) error {
|
||
// 2. 查询银行卡是否存在且属于当前用户
|
||
var bankcard model.Bankcard
|
||
if err := tx.Where("user_id = ? AND bankcard_number = ?",
|
||
req.UserID, req.BankcardNumber).First(&bankcard).Error; err != nil {
|
||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||
return errors.New("银行卡不存在或不属于当前用户")
|
||
}
|
||
return fmt.Errorf("查询银行卡信息失败: %w", err)
|
||
}
|
||
|
||
// 3. 检查是否为默认卡,若为默认卡需要特殊处理
|
||
if bankcard.IsDefault {
|
||
// 3.1 查询用户是否还有其他银行卡
|
||
var otherCount int64
|
||
if err := tx.Model(&model.Bankcard{}).
|
||
Where("user_id = ? AND bankcard_number != ?",
|
||
req.UserID, req.BankcardNumber).
|
||
Count(&otherCount).Error; err != nil {
|
||
return fmt.Errorf("查询用户其他银行卡失败: %w", err)
|
||
}
|
||
|
||
// 3.2 若还有其他卡,需要指定一张作为新默认卡
|
||
if otherCount > 0 {
|
||
var firstBankcard model.Bankcard
|
||
if err := tx.Model(&model.Bankcard{}).
|
||
Where("user_id = ? AND bankcard_number != ?",
|
||
req.UserID, req.BankcardNumber).
|
||
First(&firstBankcard).Error; err != nil {
|
||
return fmt.Errorf("查询替代默认卡失败: %w", err)
|
||
}
|
||
|
||
// 设置新默认卡
|
||
if err := tx.Model(&model.Bankcard{}).
|
||
Where("id = ?", firstBankcard.ID).
|
||
Update("is_default", true).Error; err != nil {
|
||
return fmt.Errorf("更新默认卡失败: %w", err)
|
||
}
|
||
}
|
||
// 若没有其他卡,删除后用户将没有默认卡(允许这种状态)
|
||
}
|
||
|
||
// 4. 执行删除操作
|
||
if err := tx.Where("id = ?", bankcard.ID).Delete(&model.Bankcard{}).Error; err != nil {
|
||
return fmt.Errorf("删除银行卡失败: %w", err)
|
||
}
|
||
|
||
return nil
|
||
})
|
||
}
|
||
|
||
// 验证银行卡删除请求参数
|
||
func validateRemoveBankcardRequest(req user_assets.RemoveBankcardRequest) error {
|
||
if req.UserID == "" {
|
||
return errors.New("用户ID不能为空")
|
||
}
|
||
if req.BankcardNumber == "" {
|
||
return errors.New("银行卡号不能为空")
|
||
}
|
||
// 验证银行卡号格式
|
||
if !bankcardRegex.MatchString(req.BankcardNumber) {
|
||
return errors.New("银行卡号格式错误,应为16-19位数字")
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// ListBankcardByUser 分页查询用户的银行卡列表
|
||
func ListBankcardByUser(req user_assets.ListBankcardByUserRequest) ([]*user_assets.Bankcard, int32, error) {
|
||
// 1. 参数验证与处理
|
||
if req.UserID == "" {
|
||
return nil, 0, errors.New("用户ID不能为空")
|
||
}
|
||
|
||
// 处理默认分页参数
|
||
page := req.Page
|
||
if page <= 0 {
|
||
page = 1 // 默认第一页
|
||
}
|
||
pageSize := req.PageSize
|
||
if pageSize <= 0 || pageSize > 100 {
|
||
pageSize = 10 // 默认每页10条,最大100条
|
||
}
|
||
offset := (page - 1) * pageSize
|
||
|
||
// 2. 查询总记录数
|
||
var total int64
|
||
if err := DB.Model(&model.Bankcard{}).
|
||
Where("user_id = ?", req.UserID).
|
||
Count(&total).Error; err != nil {
|
||
return nil, 0, fmt.Errorf("查询银行卡总数失败: %w", err)
|
||
}
|
||
|
||
// 3. 查询当前页数据
|
||
var bankcards []*model.Bankcard
|
||
if err := DB.Where("user_id = ?", req.UserID).
|
||
Order("is_default DESC, created_at DESC"). // 默认卡优先,按创建时间倒序
|
||
Limit(int(pageSize)).
|
||
Offset(int(offset)).
|
||
Find(&bankcards).Error; err != nil {
|
||
return nil, 0, fmt.Errorf("查询银行卡列表失败: %w", err)
|
||
}
|
||
|
||
// 4. 转换为响应结构体
|
||
var resp []*user_assets.Bankcard
|
||
for _, card := range bankcards {
|
||
resp = append(resp, &user_assets.Bankcard{
|
||
UserID: card.UserID,
|
||
BankcardNumber: maskBankcard(card.BankCardNumber), // 脱敏处理
|
||
BankName: card.BankName,
|
||
AccountType: card.AccountType,
|
||
IsDefault: card.IsDefault,
|
||
AccountName: card.AccountName,
|
||
})
|
||
}
|
||
|
||
return resp, int32(total), nil
|
||
}
|
||
|
||
// 银行卡号脱敏处理(保留前4位和后4位)
|
||
func maskBankcard(bankcard string) string {
|
||
if len(bankcard) <= 8 {
|
||
return "****"
|
||
}
|
||
return bankcard[:4] + "****" + bankcard[len(bankcard)-4:]
|
||
}
|
||
|
||
// ListConsumptionByBankcard 根据银行卡号查询消费记录列表
|
||
func ListConsumptionByBankcard(req user_assets.ListConsumptionByBankcardRequest) ([]*user_assets.ConsumptionRecord, int32, error) {
|
||
// 1. 参数验证
|
||
if err := validateListConsumptionParams(req); err != nil {
|
||
return nil, 0, fmt.Errorf("参数验证失败: %w", err)
|
||
}
|
||
|
||
// 2. 处理分页参数
|
||
page := req.Page
|
||
if page <= 0 {
|
||
page = 1
|
||
}
|
||
pageSize := req.PageSize
|
||
if pageSize <= 0 || pageSize > 100 {
|
||
pageSize = 10 // 限制最大每页数量
|
||
}
|
||
offset := (page - 1) * pageSize
|
||
|
||
// 3. 查询银行卡信息,验证存在性和归属关系
|
||
var bankcard model.Bankcard
|
||
if err := DB.Where("bankcard_number = ?", req.BankcardNumber).First(&bankcard).Error; err != nil {
|
||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||
return nil, 0, errors.New("银行卡不存在")
|
||
}
|
||
return nil, 0, fmt.Errorf("查询银行卡信息失败: %w", err)
|
||
}
|
||
|
||
// 4. 验证用户ID(如果指定)
|
||
if req.UserID != "" && bankcard.UserID != req.UserID {
|
||
return nil, 0, errors.New("银行卡不属于指定用户")
|
||
}
|
||
targetUserID := bankcard.UserID
|
||
|
||
// 5. 构建查询条件:用户ID + 银行卡来源类型
|
||
query := DB.Model(&model.ConsumptionRecord{}).
|
||
Where("user_id = ? AND source_type = ? AND remark= ?", targetUserID, model.SourceTypeBankcard, req.BankcardNumber)
|
||
|
||
// 6. 查询总记录数
|
||
var total int64
|
||
if err := query.Count(&total).Error; err != nil {
|
||
return nil, 0, fmt.Errorf("统计消费记录总数失败: %w", err)
|
||
}
|
||
|
||
// 7. 分页查询消费记录
|
||
var dbRecords []*model.ConsumptionRecord
|
||
if err := query.
|
||
Order("created_at DESC"). // 按创建时间倒序,最新记录在前
|
||
Limit(int(pageSize)).
|
||
Offset(int(offset)).
|
||
Find(&dbRecords).Error; err != nil {
|
||
return nil, 0, fmt.Errorf("查询消费记录失败: %w", err)
|
||
}
|
||
|
||
// 8. 转换为Thrift响应结构体
|
||
respRecords := make([]*user_assets.ConsumptionRecord, 0, len(dbRecords))
|
||
for _, dbRec := range dbRecords {
|
||
respRec := &user_assets.ConsumptionRecord{
|
||
UserID: dbRec.UserID,
|
||
ItemType: int8(dbRec.ItemType),
|
||
Amount: dbRec.Amount.String(),
|
||
BeforeAmount: dbRec.BeforeAmount.String(),
|
||
AfterAmount: dbRec.AfterAmount.String(),
|
||
BusinessType: dbRec.BusinessType,
|
||
BusinessID: dbRec.BusinessID,
|
||
SourceType: int8(dbRec.SourceType),
|
||
Remark: dbRec.Remark,
|
||
Operator: dbRec.Operator,
|
||
}
|
||
respRecords = append(respRecords, respRec)
|
||
}
|
||
|
||
return respRecords, int32(total), nil
|
||
}
|
||
|
||
// 参数验证函数
|
||
func validateListConsumptionParams(req user_assets.ListConsumptionByBankcardRequest) error {
|
||
if req.BankcardNumber == "" {
|
||
return errors.New("银行卡号不能为空")
|
||
}
|
||
if !bankcardRegex.MatchString(req.BankcardNumber) {
|
||
return errors.New("银行卡号格式错误,应为16-19位数字")
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// ListConsumptionByUser 根据用户ID查询消费记录列表
|
||
func ListConsumptionByUser(req user_assets.ListConsumptionByUserRequest) ([]*user_assets.ConsumptionRecord, int32, error) {
|
||
// 1. 参数验证
|
||
if err := validateListConsumptionByUserParams(req); err != nil {
|
||
return nil, 0, fmt.Errorf("参数验证失败: %w", err)
|
||
}
|
||
|
||
// 2. 处理分页参数
|
||
page := req.Page
|
||
if page <= 0 {
|
||
page = 1 // 默认第一页
|
||
}
|
||
pageSize := req.PageSize
|
||
if pageSize <= 0 || pageSize > 100 {
|
||
pageSize = 10 // 限制最大每页数量
|
||
}
|
||
offset := (page - 1) * pageSize
|
||
|
||
// 3. 构建查询条件:用户ID
|
||
query := DB.Model(&model.ConsumptionRecord{}).
|
||
Where("user_id = ?", req.UserID)
|
||
|
||
// 4. 查询总记录数
|
||
var total int64
|
||
if err := query.Count(&total).Error; err != nil {
|
||
return nil, 0, fmt.Errorf("统计消费记录总数失败: %w", err)
|
||
}
|
||
|
||
// 5. 分页查询消费记录
|
||
var dbRecords []*model.ConsumptionRecord
|
||
if err := query.
|
||
Order("created_at DESC"). // 按创建时间倒序,最新记录在前
|
||
Limit(int(pageSize)).
|
||
Offset(int(offset)).
|
||
Find(&dbRecords).Error; err != nil {
|
||
return nil, 0, fmt.Errorf("查询消费记录失败: %w", err)
|
||
}
|
||
|
||
// 6. 转换为Thrift响应结构体
|
||
respRecords := make([]*user_assets.ConsumptionRecord, 0, len(dbRecords))
|
||
for _, dbRec := range dbRecords {
|
||
respRec := &user_assets.ConsumptionRecord{
|
||
UserID: dbRec.UserID,
|
||
ItemType: int8(dbRec.ItemType),
|
||
Amount: dbRec.Amount.String(),
|
||
BeforeAmount: dbRec.BeforeAmount.String(),
|
||
AfterAmount: dbRec.AfterAmount.String(),
|
||
BusinessType: dbRec.BusinessType,
|
||
BusinessID: dbRec.BusinessID,
|
||
SourceType: int8(dbRec.SourceType),
|
||
Remark: dbRec.Remark,
|
||
Operator: dbRec.Operator,
|
||
}
|
||
respRecords = append(respRecords, respRec)
|
||
}
|
||
|
||
return respRecords, int32(total), nil
|
||
}
|
||
|
||
// 参数验证函数
|
||
func validateListConsumptionByUserParams(req user_assets.ListConsumptionByUserRequest) error {
|
||
if req.UserID == "" {
|
||
return errors.New("用户ID不能为空")
|
||
}
|
||
return nil
|
||
}
|