feat(assets): 去掉企业资产范围 256 条规则上限

逐个 IP / 域名录范围的企业很容易撞上这个上限,撞上之后只能拆成多个企业,
而范围本身并不因为条数多就更慢。

- db:删掉 MaxCompanyScopeRules 常量、请求条数校验与
  ensureCompanyScopeCapacityTx(每企业持久化总数的事务内校验)
- 前端:去掉 MAX_COMPANY_SCOPE_RULES 与「剩余额度」计算,追加范围弹窗不再
  按剩余条数报错;demo mock 的两处上限一并移除
- 单条规则仍限 1024 字符;请求正文 2 MiB(maxCompanyMutationBodyBytes)保留
  作兜底,约合四五万条规则

测试:原先断言「超容量被拒」的两个用例翻转为「超过旧上限也能写入」——
db 侧批量写 300 条 + 追加 1 条,server 侧一次创建带 300 条范围的企业。
新用例先注册清理再断言,避免断言失败把企业留在共享开发库里。
This commit is contained in:
Autumn-27 committed 2026-08-31 11:22:34 -04:00
1 parent 48e14e8cf2
commit acd1ac29d7
7 files changed
+41 -64

No files matched your search

+4 -27
View File
@@ -52,9 +52,9 @@ var (
)
const (
// MaxCompanyScopeRules bounds both request processing and the total number
// of persisted rules for one company.
MaxCompanyScopeRules = 256
// 企业范围不限制规则条数:逐个 IP / 域名录入的范围动辄上千条,封顶只会逼用户
// 拆成多个企业。请求体大小(server 侧 maxCompanyMutationBodyBytes)仍然兜底。
//
// Raw and normalized textual scope payloads are bounded by Unicode rune
// count so multi-byte input is treated consistently by the API and DB layer.
MaxCompanyScopeRawRunes = 1024
@@ -69,12 +69,8 @@ func (e *CompanyScopeValidationError) Error() string { return e.Message }
// ValidateCompanyScopeInputBounds applies request-wide limits before parsing.
// Store methods call it again so non-HTTP callers cannot bypass the limits.
// 只约束单条规则的长度,不限制条数。
func ValidateCompanyScopeInputBounds(inputs []ScopeInput) error {
if len(inputs) > MaxCompanyScopeRules {
return &CompanyScopeValidationError{Message: fmt.Sprintf(
"企业范围规则过多: 最多 %d 条", MaxCompanyScopeRules,
)}
}
for i, input := range inputs {
if utf8.RuneCountInString(input.Value) > MaxCompanyScopeRawRunes {
return &CompanyScopeValidationError{Message: fmt.Sprintf(
@@ -373,9 +369,6 @@ func (s *CompanyStore) AddScopeInputsChecked(companyID int64, inputs []ScopeInpu
if err != nil {
return 0, 0, invalid, errors, err
}
if err := ensureCompanyScopeCapacityTx(tx, companyID); err != nil {
return 0, 0, invalid, errors, err
}
if needsAttribution {
warning, err := recomputeAttributionTx(tx)
if err != nil {
@@ -430,19 +423,6 @@ func ensureCompanyExistsTx(tx *sql.Tx, companyID int64) error {
return nil
}
func ensureCompanyScopeCapacityTx(tx *sql.Tx, companyID int64) error {
var count int
if err := tx.QueryRow(`SELECT count(*) FROM company_scope WHERE company_id = $1`, companyID).Scan(&count); err != nil {
return err
}
if count > MaxCompanyScopeRules {
return &CompanyScopeValidationError{Message: fmt.Sprintf(
"企业范围规则过多: 每个企业最多 %d 条", MaxCompanyScopeRules,
)}
}
return nil
}
func lockCompanyScopeMutation(tx *sql.Tx) error {
_, err := tx.Exec(`SELECT pg_advisory_xact_lock($1)`, companyScopeMutationLock)
return err
@@ -714,9 +694,6 @@ func (s *CompanyStore) UpdateScopeInputsChecked(companyID int64, inputs []ScopeI
if err != nil {
return 0, invalid, errs, err
}
if err := ensureCompanyScopeCapacityTx(tx, companyID); err != nil {
return 0, invalid, errs, err
}
// Rebuild even for an empty replacement because removing the old rules may
// detach scope-derived assets or expose a lower-precedence company match.
warning, err := recomputeAttributionTx(tx)