``` 接收API请求,提交内容管理任务(刷新/预热/分发)到配管系统。 【输入】ContentManageRequestParam ├─ contentURLs: List // URL列表(可能包含MD5) ├─ dirs: List // 目录列表(目录刷新用) ├─ callBackUrl: String // 回调地址 └─ enterprise: String // 企业ID 【阶段1】verifyParam (行1094-1137) ↓ 校验URL数量、合法性、企业额度 ↓ 从Redis批量获取域名缓存信息 └─ 输出: Tuple4 【阶段2】checkValidDomainRefresh (行985-1078) ↓ 校验域名归属、状态、平面信息 ↓ 精准域名(数据库) + 广义域名(泛域名匹配) + 冲突域名处理 ↓ 缓存miss时查DB,写回Redis(TTL 1800秒) └─ 输出: Map<域名, 平面JSON字符串> 【阶段3】buildContentTask (行856-930) ↓ 查询企业信息(cpId, 创建者, 省份) ↓ 构建BPM任务列表(每个URL一个task) ↓ 如果有回调URL,构建回调任务PO └─ 输出: Tuple3 【阶段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) - 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 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秒 ```