Files
notes/work/移动杭研/开发记录/7.15.0/内容管理逻辑梳理1.md
T
2026-03-01 01:43:46 +08:00

6.9 KiB

  接收API请求,提交内容管理任务(刷新/预热/分发)到配管系统。

  【输入】ContentManageRequestParam
    ├─ contentURLs: List<UrlMd5>       // URL列表(可能包含MD5)
    ├─ dirs: List<String>              // 目录列表(目录刷新用)
    ├─ callBackUrl: String             // 回调地址
    └─ enterprise: String              // 企业ID

  【阶段1】verifyParam (行1094-1137)
    ↓ 校验URL数量、合法性、企业额度
    ↓ 从Redis批量获取域名缓存信息
    └─ 输出: Tuple4<URL列表, URL→域名映射, 域名集合, 缓存域名信息>

  【阶段2】checkValidDomainRefresh (行985-1078)
    ↓ 校验域名归属、状态、平面信息
    ↓ 精准域名(数据库) + 广义域名(泛域名匹配) + 冲突域名处理
    ↓ 缓存miss时查DB,写回Redis(TTL 1800秒)
    └─ 输出: Map<域名, 平面JSON字符串>

  【阶段3】buildContentTask (行856-930)
    ↓ 查询企业信息(cpId, 创建者, 省份)
    ↓ 构建BPM任务列表(每个URL一个task)
    ↓ 如果有回调URL,构建回调任务PO
    └─ 输出: Tuple3<BPM请求, 回调任务PO, 回调URL列表PO>

  【阶段4】sendContentTask (行787-848)
    ↓ 根据操作类型调用BPM接口:
    │   INSERT    → contentReload (预热)
    │   DISTRIBUTE → contentDistribution (分发)
    │   其他       → contentRefresh (刷新)
    ↓ 成功后:
    │   1. 扣减企业额度
    │   2. 保存回调任务到DB (content_customize_task)
    │   3. 保存回调URL明细到DB (content_customize_url)
    └─ 输出: taskId

  ---
  三、核心数据表结构

  1. content_customize_task (回调任务主表)

  关键字段:
  - id              主键
  - cpId            企业cpId (纯数字)
  - taskId          任务ID (UUID)
  - callBackUrl     回调地址
  - operationType   操作类型枚举
  - state           任务状态 (0=待回调)
  - callBack        回调状态 (0=未回调, 1=已回调)
  - callBackSeparate 是否单独回调

  用途: 记录需要异步回调的任务,供定时任务扫描并回调客户接口。

  2. content_customize_url (回调URL明细表)

  关键字段:
  - id              主键
  - taskId          任务ID (外键关联task表)
  - url             具体URL
  - operateType     操作类型 (0=预热,1=刷新,3=分发,4=目录刷新)
  - urlIndex        URL序号
  - issuePlainInfo  平面信息JSON (List<Integer>)
  - status          URL执行状态
  - nodeTotal       节点总数
  - nodeSuccessCount 成功节点数

  用途: 记录每个URL的执行详情,回调时提供细粒度状态。

  ---
  四、校验规则详解

  1. URL数量校验 (verifyParam)

  普通操作(刷新/预热/分发): 单次最大 maxDir(配置,默认2000)
  目录刷新: 单次最大 maxDirLength(配置,默认100)

  2. URL合法性校验 (checkURL方法,未展示)

  - URL格式验证
  - 协议检查(http/https)
  - 域名提取

  3. 企业额度校验 (enterpriseContentNumberMaxNum)

  每日默认额度:
  - 普通操作: 2000次 (Constants.CONTENT_MAXOPERATION_DAY)
  - 目录刷新: 100次 (Constants.CONTENT_MAXDIRS_DAY)
  校验逻辑: 已用额度 + 本次请求数量 <= 企业配额

  4. 域名归属校验 (checkValidDomainRefresh:1069-1072)

  if (!enterpriseId.equals(domainInfo.getTenantId())) {
      throw new PlatformException("域名:" + domain + "不存在");
  }
  关键: 防止企业操作其他企业的域名。

  5. 域名状态校验 (checkValidDomainRefresh:1064-1067)

  允许的域名状态:
  - 已生效
  - 部署中
  - 部署失败
  - 启用中

  校验方法: DomainTicketStateEnum.judgeDomainState(domainInfo.getDomainState())

  6. 域名类型处理

  - 精准域名: 完全匹配 (http://www.example.com)
  - 广义域名: 泛域名匹配 (*.example.com)
  - 冲突域名: 多企业共用域名,通过cpDomain字段区分真实域名

  ---
  五、关键业务逻辑

  1. 域名缓存策略 (行1118-1133)

  查询顺序:
  Redis批量查询 → 缓存命中直接用
                → 缓存miss查DB → 写回Redis(TTL 30分钟)

  广义域名永远查内存(不缓存到Redis的key)

  问题: 广义域名每次都查全量,如果广义域名数量巨大会有性能问题。

  2. 平面信息处理

  平面 = CDN节点分组概念 (可能是地域/运营商等维度)

  domainPlain.get(urlDomainMap.get(url))  // JSON字符串 "[1,2,3]"
      ↓ 解析
  List<Integer> plains = JSONArray.parseArray(...) // [1,2,3]

  每个URL会下发到对应的平面节点。

  3. 操作类型路由 (handleContentTask:819-829)

  switch (operationType) {
      case INSERT:     → bpm.contentReload()       // 预热
      case DISTRIBUTE: → bpm.contentDistribution() // 分发
      default:         → bpm.contentRefresh()      // 刷新/其他
  }

  注意: DIRUPDATE(目录刷新)走的是default分支,即contentRefresh()。

  ---
  六、异常处理机制

  任何阶段失败 → 抛出PlatformException → 整个任务失败
                                        → 不会保存任何DB记录
                                        → 不会扣减企业额度

  成功下发BPM后 → 立即扣减额度 + 保存回调任务
                 → 如果回调任务保存失败?代码没处理这种情况!(bug风险)

  ---
  七、代码问题清单

  🔴 严重问题

  4. Tuple滥用: Tuple4/Tuple3完全不可读,应该用命名类
  5. 重复遍历:
    - verifyParam遍历urlHostList构建urlHostMap
    - buildContentTask又遍历contentUrlList
    - 同样的数据被多次转换
  6. 异常处理不完整:

  // 行838-848
  postSuccessActions(...) {
      updateEnterpriseContentNumber(...);  // 如果这里失败?
      contentCustomizeTaskDao.save(...);   // 如果这里失败?
      contentCustomizeUrlDao.saveAll(...); // 如果这里失败?
  }
  这三个操作应该在一个事务里!

  7. 缓存一致性风险:
    - Redis缓存30分钟
    - 如果DB中域名状态变更,缓存不会主动失效
    - 可能导致已下线域名仍能操作

  🟡 中等问题

  8. 日志信息不足:
    - 缺少域名校验失败的详细日志
    - 缺少企业额度扣减的审计日志
  9. 魔法数字:
    - Redis TTL硬编码1800L
    - 应该定义常量
  10. 空指针风险:
  // 行884
  user = enterpriseInfo.getAccounts().get(0);  // 如果accounts为空?

  🟢 小问题

  11. 变量命名混乱:
    - urlPlain 实际是 domainPlainMap
    - urlHost 实际是 validationResult
  12. 注释质量差:
    - "校验url数量、url合法性..." 这不是废话吗?

  ---
  八、数据依赖关系

  EnterpriseInfo (企业表)
    └─ cpId, accounts, 额度信息

  SelfServiceDomainConfig (域名配置表)
    ├─ domain (域名)
    ├─ cpDomain (冲突域名真实值)
    ├─ tenantId (所属企业)
    ├─ domainState (域名状态)
    └─ domainPlain (平面信息JSON)

  Redis缓存
    key: content:domain:{domain}:{enterpriseId}
    value: DomainEnterpriseInfoRedisPO
    ttl: 1800秒