addui
This commit is contained in:
@@ -33,8 +33,8 @@ func ProcessIVYZ81NCRequest(ctx context.Context, params []byte, deps *processors
|
||||
|
||||
reqData := map[string]interface{}{
|
||||
"data": map[string]interface{}{
|
||||
"name": encryptedName,
|
||||
"idcard": encryptedIDCard,
|
||||
"name": encryptedName,
|
||||
"idcard": encryptedIDCard,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -62,4 +62,4 @@ func ProcessIVYZ81NCRequest(ctx context.Context, params []byte, deps *processors
|
||||
}
|
||||
|
||||
return respBytes, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,23 +165,35 @@ func (r *RechargeRecord) SetTransferOrderID(orderID string) {
|
||||
|
||||
// NewAlipayRechargeRecord 工厂方法 - 创建支付宝充值记录
|
||||
func NewAlipayRechargeRecord(userID string, amount decimal.Decimal, alipayOrderID string) *RechargeRecord {
|
||||
return NewAlipayRechargeRecordWithNotes(userID, amount, alipayOrderID, "")
|
||||
}
|
||||
|
||||
// NewAlipayRechargeRecordWithNotes 工厂方法 - 创建支付宝充值记录(带备注)
|
||||
func NewAlipayRechargeRecordWithNotes(userID string, amount decimal.Decimal, alipayOrderID, notes string) *RechargeRecord {
|
||||
return &RechargeRecord{
|
||||
UserID: userID,
|
||||
Amount: amount,
|
||||
RechargeType: RechargeTypeAlipay,
|
||||
Status: RechargeStatusPending,
|
||||
AlipayOrderID: &alipayOrderID,
|
||||
Notes: notes,
|
||||
}
|
||||
}
|
||||
|
||||
// NewWechatRechargeRecord 工厂方法 - 创建微信充值记录
|
||||
func NewWechatRechargeRecord(userID string, amount decimal.Decimal, wechatOrderID string) *RechargeRecord {
|
||||
return NewWechatRechargeRecordWithNotes(userID, amount, wechatOrderID, "")
|
||||
}
|
||||
|
||||
// NewWechatRechargeRecordWithNotes 工厂方法 - 创建微信充值记录(带备注)
|
||||
func NewWechatRechargeRecordWithNotes(userID string, amount decimal.Decimal, wechatOrderID, notes string) *RechargeRecord {
|
||||
return &RechargeRecord{
|
||||
UserID: userID,
|
||||
Amount: amount,
|
||||
RechargeType: RechargeTypeWechat,
|
||||
Status: RechargeStatusPending,
|
||||
WechatOrderID: &wechatOrderID,
|
||||
Notes: notes,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package services
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
"go.uber.org/zap"
|
||||
@@ -295,8 +296,21 @@ func (s *RechargeRecordServiceImpl) HandleAlipayPaymentSuccess(ctx context.Conte
|
||||
return nil
|
||||
}
|
||||
|
||||
// 计算充值赠送金额
|
||||
bonusAmount := calculateAlipayRechargeBonus(amount, &s.cfg.Wallet)
|
||||
// 检查是否是组件报告下载订单(通过备注判断)
|
||||
isComponentReportOrder := strings.Contains(rechargeRecord.Notes, "购买") && strings.Contains(rechargeRecord.Notes, "报告示例")
|
||||
|
||||
s.logger.Info("处理支付宝支付成功回调",
|
||||
zap.String("out_trade_no", outTradeNo),
|
||||
zap.String("recharge_id", rechargeRecord.ID),
|
||||
zap.String("notes", rechargeRecord.Notes),
|
||||
zap.Bool("is_component_report", isComponentReportOrder),
|
||||
)
|
||||
|
||||
// 计算充值赠送金额(组件报告下载订单不需要赠送)
|
||||
bonusAmount := decimal.Zero
|
||||
if !isComponentReportOrder {
|
||||
bonusAmount = calculateAlipayRechargeBonus(amount, &s.cfg.Wallet)
|
||||
}
|
||||
totalAmount := amount.Add(bonusAmount)
|
||||
|
||||
// 在事务中执行所有更新操作
|
||||
@@ -309,14 +323,22 @@ func (s *RechargeRecordServiceImpl) HandleAlipayPaymentSuccess(ctx context.Conte
|
||||
return err
|
||||
}
|
||||
|
||||
// 更新充值记录状态为成功
|
||||
rechargeRecord.MarkSuccess()
|
||||
err = s.rechargeRecordRepo.Update(txCtx, rechargeRecord)
|
||||
// 更新充值记录状态为成功(使用UpdateStatus方法直接更新状态字段)
|
||||
err = s.rechargeRecordRepo.UpdateStatus(txCtx, rechargeRecord.ID, entities.RechargeStatusSuccess)
|
||||
if err != nil {
|
||||
s.logger.Error("更新充值记录状态失败", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
|
||||
// 如果是组件报告下载订单,不增加钱包余额,不创建赠送记录
|
||||
if isComponentReportOrder {
|
||||
s.logger.Info("组件报告下载订单,跳过钱包余额增加和赠送",
|
||||
zap.String("out_trade_no", outTradeNo),
|
||||
zap.String("recharge_id", rechargeRecord.ID),
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 如果有赠送金额,创建赠送充值记录
|
||||
if bonusAmount.GreaterThan(decimal.Zero) {
|
||||
giftRechargeRecord := entities.NewGiftRechargeRecord(rechargeRecord.UserID, bonusAmount, "充值活动赠送")
|
||||
@@ -355,6 +377,10 @@ func (s *RechargeRecordServiceImpl) HandleAlipayPaymentSuccess(ctx context.Conte
|
||||
zap.String("recharge_id", rechargeRecord.ID),
|
||||
zap.String("order_id", alipayOrder.ID))
|
||||
|
||||
// 检查是否有组件报告下载记录需要更新
|
||||
// 注意:这里需要在调用方(finance应用服务)中处理,因为这里没有组件报告下载的repository
|
||||
// 但为了保持服务层的独立性,我们通过事件或回调来处理
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
package entities
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// ComponentReportCache 报告文件匹配缓存
|
||||
type ComponentReportCache struct {
|
||||
ID string `gorm:"primaryKey;type:varchar(36)" json:"id" comment:"缓存ID"`
|
||||
ProductCode string `gorm:"type:varchar(50);not null;uniqueIndex" json:"product_code" comment:"产品编号"`
|
||||
MatchedPath string `gorm:"type:varchar(500);not null" json:"matched_path" comment:"匹配到的文件夹/文件路径"`
|
||||
FileType string `gorm:"type:varchar(20);not null" json:"file_type" comment:"文件类型:folder, file"`
|
||||
CacheKey string `gorm:"type:varchar(64);not null;uniqueIndex" json:"cache_key" comment:"缓存键"`
|
||||
|
||||
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at" comment:"创建时间"`
|
||||
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at" comment:"更新时间"`
|
||||
}
|
||||
|
||||
// TableName 指定数据库表名
|
||||
func (ComponentReportCache) TableName() string {
|
||||
return "component_report_cache"
|
||||
}
|
||||
|
||||
// BeforeCreate GORM钩子:创建前自动生成UUID
|
||||
func (c *ComponentReportCache) BeforeCreate(tx *gorm.DB) error {
|
||||
if c.ID == "" {
|
||||
c.ID = uuid.New().String()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -10,30 +10,30 @@ import (
|
||||
|
||||
// ComponentReportDownload 组件报告下载记录
|
||||
type ComponentReportDownload struct {
|
||||
ID string `gorm:"primaryKey;type:varchar(36)" json:"id" comment:"下载记录ID"`
|
||||
UserID string `gorm:"type:varchar(36);not null;index" json:"user_id" comment:"用户ID"`
|
||||
ProductID string `gorm:"type:varchar(36);not null;index" json:"product_id" comment:"产品ID"`
|
||||
ProductCode string `gorm:"type:varchar(50);not null;index" json:"product_code" comment:"产品编号"`
|
||||
SubProductIDs string `gorm:"type:text" json:"sub_product_ids" comment:"子产品ID列表(JSON数组)"`
|
||||
SubProductCodes string `gorm:"type:text" json:"sub_product_codes" comment:"子产品编号列表(JSON数组)"`
|
||||
DownloadPrice decimal.Decimal `gorm:"type:decimal(10,2);not null" json:"download_price" comment:"实际支付价格"`
|
||||
OriginalPrice decimal.Decimal `gorm:"type:decimal(10,2);not null" json:"original_price" comment:"原始总价"`
|
||||
DiscountAmount decimal.Decimal `gorm:"type:decimal(10,2);default:0" json:"discount_amount" comment:"减免金额"`
|
||||
PaymentOrderID *string `gorm:"type:varchar(64)" json:"payment_order_id,omitempty" comment:"支付订单号"`
|
||||
PaymentType *string `gorm:"type:varchar(20)" json:"payment_type,omitempty" comment:"支付类型:alipay, wechat"`
|
||||
PaymentStatus string `gorm:"type:varchar(20);default:'pending';index" json:"payment_status" comment:"支付状态:pending, success, failed"`
|
||||
FilePath *string `gorm:"type:varchar(500)" json:"file_path,omitempty" comment:"生成的ZIP文件路径"`
|
||||
FileHash *string `gorm:"type:varchar(64)" json:"file_hash,omitempty" comment:"文件哈希值"`
|
||||
DownloadCount int `gorm:"default:0" json:"download_count" comment:"下载次数"`
|
||||
LastDownloadAt *time.Time `json:"last_download_at,omitempty" comment:"最后下载时间"`
|
||||
ExpiresAt *time.Time `gorm:"index" json:"expires_at,omitempty" comment:"下载有效期"`
|
||||
ID string `gorm:"primaryKey;type:varchar(36)" comment:"下载记录ID"`
|
||||
UserID string `gorm:"type:varchar(36);not null;index" comment:"用户ID"`
|
||||
ProductID string `gorm:"type:varchar(36);not null;index" comment:"产品ID"`
|
||||
ProductCode string `gorm:"type:varchar(50);not null;index" comment:"产品编号"`
|
||||
SubProductIDs string `gorm:"type:text" comment:"子产品ID列表(JSON数组,组合包使用)"`
|
||||
SubProductCodes string `gorm:"type:text" comment:"子产品编号列表(JSON数组)"`
|
||||
DownloadPrice decimal.Decimal `gorm:"type:decimal(10,2);not null" comment:"实际支付价格"`
|
||||
OriginalPrice decimal.Decimal `gorm:"type:decimal(10,2);not null" comment:"原始总价"`
|
||||
DiscountAmount decimal.Decimal `gorm:"type:decimal(10,2);default:0" comment:"减免金额"`
|
||||
PaymentOrderID *string `gorm:"type:varchar(64);index" comment:"支付订单号(关联充值记录)"`
|
||||
PaymentType *string `gorm:"type:varchar(20)" comment:"支付类型:alipay, wechat"`
|
||||
PaymentStatus string `gorm:"type:varchar(20);default:'pending';index" comment:"支付状态:pending, success, failed"`
|
||||
FilePath *string `gorm:"type:varchar(500)" comment:"生成的ZIP文件路径(用于二次下载)"`
|
||||
FileHash *string `gorm:"type:varchar(64)" comment:"文件哈希值(用于缓存验证)"`
|
||||
DownloadCount int `gorm:"default:0" comment:"下载次数"`
|
||||
LastDownloadAt *time.Time `comment:"最后下载时间"`
|
||||
ExpiresAt *time.Time `gorm:"index" comment:"下载有效期(支付成功后30天)"`
|
||||
|
||||
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at" comment:"创建时间"`
|
||||
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at" comment:"更新时间"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"index" json:"-" comment:"软删除时间"`
|
||||
CreatedAt time.Time `gorm:"autoCreateTime" comment:"创建时间"`
|
||||
UpdatedAt time.Time `gorm:"autoUpdateTime" comment:"更新时间"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"index" comment:"软删除时间"`
|
||||
}
|
||||
|
||||
// TableName 指定数据库表名
|
||||
// TableName 指定表名
|
||||
func (ComponentReportDownload) TableName() string {
|
||||
return "component_report_downloads"
|
||||
}
|
||||
@@ -46,8 +46,8 @@ func (c *ComponentReportDownload) BeforeCreate(tx *gorm.DB) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsPaymentSuccess 检查是否支付成功
|
||||
func (c *ComponentReportDownload) IsPaymentSuccess() bool {
|
||||
// IsPaid 检查是否已支付
|
||||
func (c *ComponentReportDownload) IsPaid() bool {
|
||||
return c.PaymentStatus == "success"
|
||||
}
|
||||
|
||||
@@ -61,25 +61,5 @@ func (c *ComponentReportDownload) IsExpired() bool {
|
||||
|
||||
// CanDownload 检查是否可以下载
|
||||
func (c *ComponentReportDownload) CanDownload() bool {
|
||||
return c.IsPaymentSuccess() && !c.IsExpired()
|
||||
return c.IsPaid() && !c.IsExpired()
|
||||
}
|
||||
|
||||
// MarkPaymentSuccess 标记支付成功
|
||||
func (c *ComponentReportDownload) MarkPaymentSuccess(orderID string, paymentType string) {
|
||||
c.PaymentOrderID = &orderID
|
||||
paymentTypeStr := paymentType
|
||||
c.PaymentType = &paymentTypeStr
|
||||
c.PaymentStatus = "success"
|
||||
|
||||
// 设置30天有效期
|
||||
expiresAt := time.Now().Add(30 * 24 * time.Hour)
|
||||
c.ExpiresAt = &expiresAt
|
||||
}
|
||||
|
||||
// IncrementDownloadCount 增加下载次数
|
||||
func (c *ComponentReportDownload) IncrementDownloadCount() {
|
||||
c.DownloadCount++
|
||||
now := time.Now()
|
||||
c.LastDownloadAt = &now
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,9 @@ type Product struct {
|
||||
IsPackage bool `gorm:"default:false" comment:"是否组合包"`
|
||||
// 组合包相关关联
|
||||
PackageItems []*ProductPackageItem `gorm:"foreignKey:PackageID" comment:"组合包项目列表"`
|
||||
// UI组件相关字段
|
||||
SellUIComponent bool `gorm:"default:false" comment:"是否出售UI组件"`
|
||||
UIComponentPrice decimal.Decimal `gorm:"type:decimal(10,2);default:0" comment:"UI组件销售价格(组合包使用)"`
|
||||
// SEO信息
|
||||
SEOTitle string `gorm:"type:varchar(200)" comment:"SEO标题"`
|
||||
SEODescription string `gorm:"type:text" comment:"SEO描述"`
|
||||
|
||||
36
internal/domains/product/entities/product_ui_component.go
Normal file
36
internal/domains/product/entities/product_ui_component.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package entities
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/shopspring/decimal"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// ProductUIComponent 产品UI组件关联实体
|
||||
type ProductUIComponent struct {
|
||||
ID string `gorm:"primaryKey;type:varchar(36)" comment:"关联ID"`
|
||||
ProductID string `gorm:"type:varchar(36);not null;index" comment:"产品ID"`
|
||||
UIComponentID string `gorm:"type:varchar(36);not null;index" comment:"UI组件ID"`
|
||||
Price decimal.Decimal `gorm:"type:decimal(10,2);not null;default:0" comment:"销售价格"`
|
||||
IsEnabled bool `gorm:"default:true" comment:"是否启用销售"`
|
||||
CreatedAt time.Time `gorm:"autoCreateTime" comment:"创建时间"`
|
||||
UpdatedAt time.Time `gorm:"autoUpdateTime" comment:"更新时间"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"index" comment:"软删除时间"`
|
||||
|
||||
// 关联关系
|
||||
Product *Product `gorm:"foreignKey:ProductID" comment:"产品"`
|
||||
UIComponent *UIComponent `gorm:"foreignKey:UIComponentID" comment:"UI组件"`
|
||||
}
|
||||
|
||||
func (ProductUIComponent) TableName() string {
|
||||
return "product_ui_components"
|
||||
}
|
||||
|
||||
func (p *ProductUIComponent) BeforeCreate(tx *gorm.DB) error {
|
||||
if p.ID == "" {
|
||||
p.ID = uuid.New().String()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
39
internal/domains/product/entities/ui_component.go
Normal file
39
internal/domains/product/entities/ui_component.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package entities
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// UIComponent UI组件实体
|
||||
type UIComponent struct {
|
||||
ID string `gorm:"primaryKey;type:varchar(36)" json:"id" comment:"组件ID"`
|
||||
ComponentCode string `gorm:"type:varchar(50);not null;uniqueIndex" json:"component_code" comment:"组件编码"`
|
||||
ComponentName string `gorm:"type:varchar(100);not null" json:"component_name" comment:"组件名称"`
|
||||
Description string `gorm:"type:text" json:"description" comment:"组件描述"`
|
||||
FilePath *string `gorm:"type:varchar(500)" json:"file_path" comment:"组件文件路径"`
|
||||
FileHash *string `gorm:"type:varchar(64)" json:"file_hash" comment:"文件哈希值"`
|
||||
FileSize *int64 `gorm:"type:bigint" json:"file_size" comment:"文件大小"`
|
||||
FileType *string `gorm:"type:varchar(50)" json:"file_type" comment:"文件类型"`
|
||||
FolderPath *string `gorm:"type:varchar(500)" json:"folder_path" comment:"组件文件夹路径"`
|
||||
IsExtracted bool `gorm:"default:false" json:"is_extracted" comment:"是否已解压"`
|
||||
Version string `gorm:"type:varchar(20)" json:"version" comment:"组件版本"`
|
||||
IsActive bool `gorm:"default:true" json:"is_active" comment:"是否启用"`
|
||||
SortOrder int `gorm:"default:0" json:"sort_order" comment:"排序"`
|
||||
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at" comment:"创建时间"`
|
||||
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at" comment:"更新时间"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"index" json:"deleted_at" comment:"软删除时间"`
|
||||
}
|
||||
|
||||
func (UIComponent) TableName() string {
|
||||
return "ui_components"
|
||||
}
|
||||
|
||||
func (u *UIComponent) BeforeCreate(tx *gorm.DB) error {
|
||||
if u.ID == "" {
|
||||
u.ID = uuid.New().String()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -2,23 +2,31 @@ package repositories
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"tyapi-server/internal/domains/product/entities"
|
||||
)
|
||||
|
||||
// ComponentReportRepository 组件报告仓储接口
|
||||
type ComponentReportRepository interface {
|
||||
// 下载记录相关
|
||||
// 创建下载记录
|
||||
CreateDownload(ctx context.Context, download *entities.ComponentReportDownload) (*entities.ComponentReportDownload, error)
|
||||
|
||||
// 更新下载记录
|
||||
UpdateDownload(ctx context.Context, download *entities.ComponentReportDownload) error
|
||||
|
||||
// 根据ID获取下载记录
|
||||
GetDownloadByID(ctx context.Context, id string) (*entities.ComponentReportDownload, error)
|
||||
|
||||
// 获取用户的下载记录列表
|
||||
GetUserDownloads(ctx context.Context, userID string, productID *string) ([]*entities.ComponentReportDownload, error)
|
||||
|
||||
// 检查用户是否已下载过指定产品编号的组件
|
||||
HasUserDownloaded(ctx context.Context, userID string, productCode string) (bool, error)
|
||||
|
||||
// 获取用户已下载的产品编号列表
|
||||
GetUserDownloadedProductCodes(ctx context.Context, userID string) ([]string, error)
|
||||
|
||||
// 根据支付订单号获取下载记录
|
||||
GetDownloadByPaymentOrderID(ctx context.Context, orderID string) (*entities.ComponentReportDownload, error)
|
||||
|
||||
// 缓存相关
|
||||
GetCacheByProductCode(ctx context.Context, productCode string) (*entities.ComponentReportCache, error)
|
||||
CreateCache(ctx context.Context, cache *entities.ComponentReportCache) error
|
||||
UpdateCache(ctx context.Context, cache *entities.ComponentReportCache) error
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package repositories
|
||||
|
||||
import (
|
||||
"context"
|
||||
"tyapi-server/internal/domains/product/entities"
|
||||
)
|
||||
|
||||
// ProductUIComponentRepository 产品UI组件关联仓储接口
|
||||
type ProductUIComponentRepository interface {
|
||||
Create(ctx context.Context, relation entities.ProductUIComponent) (entities.ProductUIComponent, error)
|
||||
GetByProductID(ctx context.Context, productID string) ([]entities.ProductUIComponent, error)
|
||||
GetByUIComponentID(ctx context.Context, componentID string) ([]entities.ProductUIComponent, error)
|
||||
Delete(ctx context.Context, id string) error
|
||||
DeleteByProductID(ctx context.Context, productID string) error
|
||||
BatchCreate(ctx context.Context, relations []entities.ProductUIComponent) error
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package repositories
|
||||
|
||||
import (
|
||||
"context"
|
||||
"tyapi-server/internal/domains/product/entities"
|
||||
)
|
||||
|
||||
// UIComponentRepository UI组件仓储接口
|
||||
type UIComponentRepository interface {
|
||||
Create(ctx context.Context, component entities.UIComponent) (entities.UIComponent, error)
|
||||
GetByID(ctx context.Context, id string) (*entities.UIComponent, error)
|
||||
GetByCode(ctx context.Context, code string) (*entities.UIComponent, error)
|
||||
List(ctx context.Context, filters map[string]interface{}) ([]entities.UIComponent, int64, error)
|
||||
Update(ctx context.Context, component entities.UIComponent) error
|
||||
Delete(ctx context.Context, id string) error
|
||||
GetByCodes(ctx context.Context, codes []string) ([]entities.UIComponent, error)
|
||||
}
|
||||
Reference in New Issue
Block a user