Some checks failed
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Failing after 8m39s
- 迁移 000259~000265:套餐记录沉默期字段与扫描索引、套餐默认沉默期、延期政策/流水/订单/批次/明细 5 张表、默认政策与系统配置(总开关默认关闭)、企微审批白名单、卡与设备出库时间及出库记录 - 沉默期设置(订单发放、自动购包、钱包续费)、后台购买时调整(超默认值按政策免审或企微审批)、统一判定与激活闸门、排队与孤儿扫描跳过过期套餐、过期套餐禁止退款、每 10 分钟扫描 - 总开关专用接口与打开前 4 项检查;延期政策;延期资格与计费;H5 自助与后台二维码在线支付(商户池路由)、支付回调与查单恢复、线下登记、特殊/批量延期、异常重试与原路退款;沉默期审批场景与终态处理 - 换货沉默期流水与出库时间;卡/设备列表、详情、导出与出库记录接口;延期订单与即将过期套餐列表及导出;财务流水纳入延期支付 - 沉默期与出库时间回填工具 cmd/silence-backfill;前端接口变更说明 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
133 lines
6.5 KiB
Go
133 lines
6.5 KiB
Go
package packagepkg
|
|
|
|
import (
|
|
"context"
|
|
"strconv"
|
|
|
|
"gorm.io/gorm"
|
|
|
|
"github.com/break/junhong_cmp_fiber/internal/infrastructure/audit"
|
|
"github.com/break/junhong_cmp_fiber/internal/model"
|
|
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
|
"github.com/break/junhong_cmp_fiber/pkg/errors"
|
|
)
|
|
|
|
// SetAccessAudit 注入套餐商品统一审计 Writer。
|
|
func (s *Service) SetAccessAudit(db *gorm.DB, writer *audit.Writer) {
|
|
s.db = db
|
|
s.auditWriter = writer
|
|
}
|
|
|
|
func (s *Service) appendPackageAudit(ctx context.Context, tx *gorm.DB, actionCode, summary string, pkg *model.Package, beforeData, afterData map[string]any) error {
|
|
if s.auditWriter == nil || s.db == nil {
|
|
return errors.New(errors.CodeInvalidStatus, "套餐商品统一审计接缝未配置")
|
|
}
|
|
resources := []audit.ResourceInput{
|
|
audit.PackageResource(pkg, constants.AuditResourceRelationPrimary, constants.AuditResourceRolePackageTarget, beforeData, afterData),
|
|
}
|
|
if pkg.SeriesID > 0 {
|
|
var series model.PackageSeries
|
|
if err := tx.WithContext(ctx).Unscoped().Where("id = ?", pkg.SeriesID).First(&series).Error; err != nil {
|
|
return errors.Wrap(errors.CodeDatabaseError, err, "查询套餐系列审计快照失败")
|
|
}
|
|
resources = append(resources, audit.PackageSeriesResource(&series, constants.AuditResourceRelationReference, constants.AuditResourceRolePackageSeries, nil, nil))
|
|
}
|
|
return s.auditWriter.Append(ctx, tx, audit.AppendInput{
|
|
ActionCode: actionCode, Summary: summary, ScopeType: constants.AuditScopePlatform,
|
|
Result: constants.AuditResultSuccess, Resources: resources,
|
|
})
|
|
}
|
|
|
|
func (s *Service) appendAllocationAudit(ctx context.Context, tx *gorm.DB, actionCode, summary string, allocation *model.ShopPackageAllocation, pkg *model.Package, beforeData, afterData map[string]any) error {
|
|
if s.auditWriter == nil || s.db == nil {
|
|
return errors.New(errors.CodeInvalidStatus, "店铺套餐统一审计接缝未配置")
|
|
}
|
|
resources := []audit.ResourceInput{
|
|
audit.ShopPackageAllocationResource(allocation, constants.AuditResourceRelationPrimary, constants.AuditResourceRoleShopPackageAllocation, beforeData, afterData),
|
|
audit.PackageResource(pkg, constants.AuditResourceRelationReference, constants.AuditResourceRolePackageTarget, nil, nil),
|
|
}
|
|
var shop model.Shop
|
|
if err := tx.WithContext(ctx).Unscoped().Where("id = ?", allocation.ShopID).First(&shop).Error; err != nil {
|
|
return errors.Wrap(errors.CodeDatabaseError, err, "查询店铺套餐审计快照失败")
|
|
}
|
|
resources = append(resources, audit.ShopResource(&shop, constants.AuditResourceRelationReference, constants.AuditResourceRolePackageConfigShop))
|
|
return s.auditWriter.Append(ctx, tx, audit.AppendInput{
|
|
ActionCode: actionCode, Summary: summary, ScopeType: constants.AuditScopeShop,
|
|
ScopeID: allocationShopID(allocation), Result: constants.AuditResultSuccess, Resources: resources,
|
|
})
|
|
}
|
|
|
|
func (s *Service) recordPackageFailure(ctx context.Context, actionCode, summary string, pkg *model.Package, beforeData map[string]any, businessErr error) {
|
|
if pkg == nil {
|
|
return
|
|
}
|
|
s.auditWriter.RecordFailure(ctx, s.db, audit.AppendInput{
|
|
ActionCode: actionCode, Summary: summary, ScopeType: constants.AuditScopePlatform,
|
|
Resources: []audit.ResourceInput{audit.PackageResource(
|
|
pkg, constants.AuditResourceRelationPrimary, constants.AuditResourceRolePackageTarget, beforeData, nil,
|
|
)},
|
|
}, businessErr)
|
|
}
|
|
|
|
func (s *Service) recordAllocationFailure(ctx context.Context, actionCode, summary string, allocation *model.ShopPackageAllocation, pkg *model.Package, beforeData map[string]any, businessErr error) {
|
|
if allocation == nil || pkg == nil {
|
|
return
|
|
}
|
|
shop := &model.Shop{Model: gorm.Model{ID: allocation.ShopID}}
|
|
s.auditWriter.RecordFailure(ctx, s.db, audit.AppendInput{
|
|
ActionCode: actionCode, Summary: summary, ScopeType: constants.AuditScopeShop,
|
|
ScopeID: allocationShopID(allocation), Resources: []audit.ResourceInput{
|
|
audit.ShopPackageAllocationResource(allocation, constants.AuditResourceRelationPrimary, constants.AuditResourceRoleShopPackageAllocation, beforeData, nil),
|
|
audit.PackageResource(pkg, constants.AuditResourceRelationReference, constants.AuditResourceRolePackageTarget, nil, nil),
|
|
audit.ShopResource(shop, constants.AuditResourceRelationReference, constants.AuditResourceRolePackageConfigShop),
|
|
},
|
|
}, businessErr)
|
|
}
|
|
|
|
func allocationShopID(allocation *model.ShopPackageAllocation) string {
|
|
if allocation == nil {
|
|
return ""
|
|
}
|
|
return uintString(allocation.ShopID)
|
|
}
|
|
|
|
func uintString(value uint) string {
|
|
return strconv.FormatUint(uint64(value), 10)
|
|
}
|
|
|
|
func packageData(pkg *model.Package) map[string]any {
|
|
if pkg == nil {
|
|
return nil
|
|
}
|
|
return map[string]any{
|
|
"package_name": pkg.PackageName, "series_id": pkg.SeriesID, "package_type": pkg.PackageType,
|
|
"duration_months": pkg.DurationMonths, "duration_days": pkg.DurationDays,
|
|
"real_data_mb": pkg.RealDataMB, "virtual_data_mb": pkg.VirtualDataMB,
|
|
"enable_virtual_data": pkg.EnableVirtualData, "cost_price": pkg.CostPrice,
|
|
"suggested_retail_price": pkg.SuggestedRetailPrice, "price_config_status": pkg.PriceConfigStatus,
|
|
"is_gift": pkg.IsGift, "status": pkg.Status, "shelf_status": pkg.ShelfStatus,
|
|
"calendar_type": pkg.CalendarType, "data_reset_cycle": pkg.DataResetCycle, "expiry_base": pkg.ExpiryBase,
|
|
"silence_period_days": pkg.SilencePeriodDays, "silence_extension_enabled": pkg.SilenceExtensionEnabled,
|
|
}
|
|
}
|
|
|
|
// appendSilenceDefaultAudit 在默认沉默期或是否支持延期变化时写审计 package.silence_default_changed。
|
|
// 修改默认值只影响之后的新购买,已有套餐记录的截止日不变。
|
|
func (s *Service) appendSilenceDefaultAudit(ctx context.Context, tx *gorm.DB, before, after *model.Package) error {
|
|
if sameIntPointer(before.SilencePeriodDays, after.SilencePeriodDays) && before.SilenceExtensionEnabled == after.SilenceExtensionEnabled {
|
|
return nil
|
|
}
|
|
if s.auditWriter == nil {
|
|
return nil
|
|
}
|
|
beforeData := map[string]any{"silence_period_days": before.SilencePeriodDays, "silence_extension_enabled": before.SilenceExtensionEnabled}
|
|
afterData := map[string]any{"silence_period_days": after.SilencePeriodDays, "silence_extension_enabled": after.SilenceExtensionEnabled}
|
|
resource := audit.PackageResource(after, constants.AuditResourceRelationPrimary, constants.AuditResourceRolePackageTarget, beforeData, afterData)
|
|
return s.auditWriter.AppendSilenceResource(ctx, tx, constants.AuditActionPackageSilenceDefaultChanged,
|
|
"修改套餐默认沉默期 "+after.PackageCode, resource, nil)
|
|
}
|
|
|
|
func sameIntPointer(left, right *int) bool {
|
|
return left == nil && right == nil || left != nil && right != nil && *left == *right
|
|
}
|