feat(wallet,tag): 钱包和标签系统多租户改造
核心变更: - 钱包表:删除 user_id,添加 resource_type/resource_id(绑定资源而非用户) - 标签表:添加 enterprise_id/shop_id(实现三级隔离:全局/企业/店铺) - GORM Callback:自动数据权限过滤 - 迁移脚本:可重复执行,已验证回滚功能 钱包归属重构原因: - 旧设计:钱包绑定用户账号,个人客户卡/设备转手后新用户无法使用余额 - 新设计:钱包绑定资源(卡/设备/店铺),余额随资源流转 标签三级隔离: - 平台全局标签:所有用户可见 - 企业标签:仅该企业可见(企业内唯一) - 店铺标签:该店铺及下级可见(店铺内唯一) 测试覆盖: - 9 个单元测试验证标签多租户过滤(全部通过) - 迁移和回滚功能测试通过(测试环境) - OpenSpec 验证通过 变更 ID: fix-wallet-tag-multi-tenant 迁移版本: 000008 参考: openspec/changes/archive/2026-01-13-fix-wallet-tag-multi-tenant/
This commit is contained in:
@@ -94,6 +94,30 @@ func RegisterDataPermissionCallback(db *gorm.DB, shopStore ShopStoreInterface) e
|
||||
|
||||
// 5.1 代理用户:基于店铺层级过滤
|
||||
if userType == constants.UserTypeAgent {
|
||||
tableName := schema.Table
|
||||
|
||||
// 特殊处理:标签表和资源标签表(包含全局标签)
|
||||
if tableName == "tb_tag" || tableName == "tb_resource_tag" {
|
||||
if shopID == 0 {
|
||||
// 没有 shop_id,只能看全局标签
|
||||
tx.Where("enterprise_id IS NULL AND shop_id IS NULL")
|
||||
return
|
||||
}
|
||||
|
||||
// 查询该店铺及下级店铺的 ID
|
||||
subordinateShopIDs, err := shopStore.GetSubordinateShopIDs(ctx, shopID)
|
||||
if err != nil {
|
||||
logger.GetAppLogger().Error("数据权限过滤:获取下级店铺 ID 失败",
|
||||
zap.Uint("shop_id", shopID),
|
||||
zap.Error(err))
|
||||
subordinateShopIDs = []uint{shopID}
|
||||
}
|
||||
|
||||
// 过滤:店铺标签(自己店铺及下级店铺)或全局标签
|
||||
tx.Where("shop_id IN ? OR (enterprise_id IS NULL AND shop_id IS NULL)", subordinateShopIDs)
|
||||
return
|
||||
}
|
||||
|
||||
if !hasShopIDField(schema) {
|
||||
// 表没有 shop_id 字段,无法过滤
|
||||
return
|
||||
@@ -127,6 +151,19 @@ func RegisterDataPermissionCallback(db *gorm.DB, shopStore ShopStoreInterface) e
|
||||
// 5.2 企业用户:基于 enterprise_id 过滤
|
||||
if userType == constants.UserTypeEnterprise {
|
||||
enterpriseID := middleware.GetEnterpriseIDFromContext(ctx)
|
||||
tableName := schema.Table
|
||||
|
||||
// 特殊处理:标签表和资源标签表(包含全局标签)
|
||||
if tableName == "tb_tag" || tableName == "tb_resource_tag" {
|
||||
if enterpriseID != 0 {
|
||||
// 过滤:企业标签或全局标签
|
||||
tx.Where("enterprise_id = ? OR (enterprise_id IS NULL AND shop_id IS NULL)", enterpriseID)
|
||||
} else {
|
||||
// 没有 enterprise_id,只能看全局标签
|
||||
tx.Where("enterprise_id IS NULL AND shop_id IS NULL")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if hasEnterpriseIDField(schema) {
|
||||
if enterpriseID != 0 {
|
||||
@@ -152,6 +189,13 @@ func RegisterDataPermissionCallback(db *gorm.DB, shopStore ShopStoreInterface) e
|
||||
// 5.3 个人客户:只能看自己的数据
|
||||
if userType == constants.UserTypePersonalCustomer {
|
||||
customerID := middleware.GetCustomerIDFromContext(ctx)
|
||||
tableName := schema.Table
|
||||
|
||||
// 特殊处理:标签表和资源标签表(只能看全局标签)
|
||||
if tableName == "tb_tag" || tableName == "tb_resource_tag" {
|
||||
tx.Where("enterprise_id IS NULL AND shop_id IS NULL")
|
||||
return
|
||||
}
|
||||
|
||||
// 优先使用 customer_id 字段
|
||||
if hasCustomerIDField(schema) {
|
||||
|
||||
Reference in New Issue
Block a user