You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
112 lines
4.1 KiB
112 lines
4.1 KiB
package console
|
|
|
|
import (
|
|
"yunyan/pb"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
// 品牌商账号 / 渠道商账号的数据收敛(P3 渠道分成体系)。
|
|
//
|
|
// 语义:
|
|
// - 渠道商账号(5):锁死到自己那一个渠道商——由 acctScope.statsClause 直接出 channel_id = ?;
|
|
// - 品牌商账号(3, 即代理):可见「本品牌商名下全部渠道商」的行,外加「本品牌商产品」的行
|
|
// (历史数据 channel_id 为空,只能靠产品归属找回,否则品牌商看不到自己迁移前的数据)。
|
|
//
|
|
// 两条件取 OR:任一命中都是本品牌商的数据,不会串到别的品牌商——product 与 channel 都按
|
|
// brandid 归属,交集之外的行必然不属于本品牌商。
|
|
//
|
|
// 这些收敛在后端强制执行,前端传什么过滤条件都只能在这个范围内再缩小。
|
|
|
|
// brandChannelIds 取某品牌商名下全部渠道商 id(含未启用的:历史统计行仍挂在它名下)。
|
|
func (this *serverComp) brandChannelIds(brandId uint32) []string {
|
|
sys := consoleDeviceConn()
|
|
list, err := dvChannels(sys, brandId, -1, "")
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
out := make([]string, 0, len(list))
|
|
for _, ch := range list {
|
|
out = append(out, ch.Id)
|
|
}
|
|
return out
|
|
}
|
|
|
|
// brandProductIds 取某品牌商名下全部产品 id(走产品缓存,避免每次查库)。
|
|
func (this *serverComp) brandProductIds(brandId uint32) []uint32 {
|
|
products, err := this.module.deviceCache.GetProducts()
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
out := make([]uint32, 0, 8)
|
|
for _, p := range products {
|
|
if p.Brandid == brandId {
|
|
out = append(out, p.Id)
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
// statsScopeClause 组合出统计查询的账号作用域条件:
|
|
// 先取通用收敛(应用/产品/区域/渠道商),品牌商账号再额外补上「本品牌商的渠道商 ∪ 产品」限定。
|
|
// 返回的条件由调用方与前端下钻条件 AND,越权不可能穿透。
|
|
func (this *serverComp) statsScopeClause(c *gin.Context) (string, []interface{}) {
|
|
s := scopeOf(c)
|
|
// 账号绑定里存的是应用名与国内/海外,统计表按部署粒度存,收敛前同样要展开(口径与前端下钻一致)。
|
|
s.apps = this.expandAppNames(s.apps)
|
|
s.regions = expandRegionCodes(s.regions)
|
|
where, args := s.statsClause()
|
|
// 只有「品牌商账号」需要额外收敛:超管/管理员不受限;渠道商账号已被 statsClause 锁死。
|
|
if s.unlimited || s.brandId == 0 || s.channelId != "" {
|
|
return where, args
|
|
}
|
|
channels := this.brandChannelIds(s.brandId)
|
|
products := this.brandProductIds(s.brandId)
|
|
var bw string
|
|
var ba []interface{}
|
|
switch {
|
|
case len(channels) > 0 && len(products) > 0:
|
|
bw = "(channel_id IN ? OR product_id IN ?)"
|
|
ba = []interface{}{channels, products}
|
|
case len(channels) > 0:
|
|
bw = "channel_id IN ?"
|
|
ba = []interface{}{channels}
|
|
case len(products) > 0:
|
|
bw = "product_id IN ?"
|
|
ba = []interface{}{products}
|
|
default:
|
|
// 该品牌商既无渠道商也无产品:没有任何数据属于它,返回恒假而不是「不过滤」,
|
|
// 否则会退化成看到全量。
|
|
bw = "1=0"
|
|
}
|
|
return andWhere(where, args, bw, ba)
|
|
}
|
|
|
|
// requireBrandScope 校验请求里的品牌商 id 在账号可见范围内;越界即写好错误响应并返回 false。
|
|
func (this *serverComp) requireBrandScope(c *gin.Context, brandId uint32) bool {
|
|
if scopeOf(c).allowBrand(brandId) {
|
|
return true
|
|
}
|
|
writeErr(c, pb.ErrorCode_InsufficientPermissions, "无权访问该品牌商的数据")
|
|
return false
|
|
}
|
|
|
|
// effectiveBrandId 把请求里的品牌商过滤收敛到账号绑定:品牌商/渠道商账号一律强制成自己的
|
|
// brandid(忽略前端传值),其余角色按前端传值。用于列表类查询。
|
|
func effectiveBrandId(c *gin.Context, reqBrandId uint32) uint32 {
|
|
s := scopeOf(c)
|
|
if s.unlimited || s.brandId == 0 {
|
|
return reqBrandId
|
|
}
|
|
return s.brandId
|
|
}
|
|
|
|
// effectiveChannelId 把请求里的渠道商过滤收敛到账号绑定:渠道商账号一律强制成自己的 channelid,
|
|
// 其余角色按前端传值。
|
|
func effectiveChannelId(c *gin.Context, reqChannelId string) string {
|
|
s := scopeOf(c)
|
|
if s.unlimited || s.channelId == "" {
|
|
return reqChannelId
|
|
}
|
|
return s.channelId
|
|
}
|
|
|