144 lines
21 KiB
Markdown
144 lines
21 KiB
Markdown
# UNIS CRM 系统全量审计分析报告
|
||
|
||
- 审计日期:2026-09-01
|
||
- 审计范围:后端(Spring Boot + MyBatis)、前端(frontend / frontend1 两个 React 工程)、SQL 脚本、Docker/Nginx 部署配置
|
||
- 审计方式:静态代码审查(未改动任何代码)
|
||
- 结论概览:商机/渠道/CRM 拓展互移、商机 tab 阶段筛选导出等核心业务逻辑**正确**;但存在 **2 个越权类安全漏洞、1 个前端业务阻断缺陷、1 个全新部署致命缺口** 及若干配置/一致性问题,建议按优先级修复。
|
||
|
||
---
|
||
|
||
## 一、严重问题(建议优先修复)
|
||
|
||
### 1. 打卡照片 / 日报附件下载接口存在越权访问(IDOR)
|
||
- 位置:
|
||
- [WorkController.java#L152-L159](file:///Users/kangwenjing/Downloads/crm/unis_crm/backend/src/main/java/com/unis/crm/controller/WorkController.java#L152-L159)(`GET /api/work/checkin-photos/{fileName}`)
|
||
- [WorkController.java#L169-L176](file:///Users/kangwenjing/Downloads/crm/unis_crm/backend/src/main/java/com/unis/crm/controller/WorkController.java#L169-L176)(`GET /api/work/report-attachments/{fileName}`)
|
||
- [WorkServiceImpl.java#L468-L476](file:///Users/kangwenjing/Downloads/crm/unis_crm/backend/src/main/java/com/unis/crm/service/impl/WorkServiceImpl.java#L468-L476)、[WorkServiceImpl.java#L506-L514](file:///Users/kangwenjing/Downloads/crm/unis_crm/backend/src/main/java/com/unis/crm/service/impl/WorkServiceImpl.java#L506-L514)
|
||
- 问题:两个下载接口不接收 `X-User-Id`,服务层仅做了「目录穿越」校验(拒绝 `..`、`/`、`\`),**未校验文件属主/数据权限**。文件名格式为 `{userId}-{uuid}.{ext}`(见 [WorkServiceImpl.java#L462](file:///Users/kangwenjing/Downloads/crm/unis_crm/backend/src/main/java/com/unis/crm/service/impl/WorkServiceImpl.java#L462)、[WorkServiceImpl.java#L495](file:///Users/kangwenjing/Downloads/crm/unis_crm/backend/src/main/java/com/unis/crm/service/impl/WorkServiceImpl.java#L495))。任何已登录用户只要拿到/猜到他人文件名(文件名含 userId 前缀,可枚举)即可下载其打卡照片和日报附件。
|
||
- 建议:下载时校验文件前缀 userId 与当前登录用户一致,或通过数据权限服务校验归属。
|
||
|
||
### 2. 生产配置硬编码大量敏感凭据,且 jwt-secret 使用默认值
|
||
- 位置:[application.yml#L4-L10、L20-L29、L53、L78、L88](file:///Users/kangwenjing/Downloads/crm/unis_crm/backend/src/main/resources/application.yml#L4-L10)、[application-prod.yml#L51](file:///Users/kangwenjing/Downloads/crm/unis_crm/backend/src/main/resources/application-prod.yml#L51)
|
||
- 问题:
|
||
- MinIO(`admin/Admin@123456`)、PostgreSQL(`unis@123`)、Redis(`zghz@123`)、企微 secret、OMS api-key 全部明文写入仓库。
|
||
- 两个 profile 的 `jwt-secret` 均为默认值 `change-me-please-change-me-32bytes`,且 docker-compose 未通过环境变量覆盖([docker-compose.yml#L14-L16](file:///Users/kangwenjing/Downloads/crm/unis_crm/docker-compose.yml#L14-L16) 仅传 `TZ` 与 `SPRING_PROFILES_ACTIVE`)。一旦使用默认值,攻击者可伪造任意用户/租户的 access token。
|
||
- 建议:改为注入环境变量/密钥管理,强制覆盖 `jwt-secret`。
|
||
|
||
### 3. 日报提交失败后前端永久锁死,需刷新页面才能恢复
|
||
- 位置:[Work.tsx#L2175-L2226](file:///Users/kangwenjing/Downloads/crm/unis_crm/frontend/src/pages/Work.tsx#L2175-L2226)
|
||
- 问题:`reportSubmitInFlightRef.current = true` 与 `setReportSubmitLocked(true)` 在 [L2205-L2206](file:///Users/kangwenjing/Downloads/crm/unis_crm/frontend/src/pages/Work.tsx#L2205-L2206) 设置,但只在**成功路径**(L2214-L2215)复位。一旦 `saveWorkDailyReport` 抛错(网络异常、后端校验失败、401 等),两个标记永久保持 `true`,提交按钮被守卫 [L2176](file:///Users/kangwenjing/Downloads/crm/unis_crm/frontend/src/pages/Work.tsx#L2176) 永久禁用,提示「提交未确认,请刷新后重试」。当日已填写的整份日报(行项/附件/明日计划)必须刷新页面才能再次提交,丢失风险高。
|
||
- 建议:将两个标记的复位移入 `catch` 或 `finally`(与 `setSubmittingReport(false)` 一起)。
|
||
|
||
### 4. 全新安装脚本 `init_full_pg17.sql` 缺少 4 张运行时依赖表
|
||
- 位置:[init_full_pg17.sql](file:///Users/kangwenjing/Downloads/crm/unis_crm/sql/init_full_pg17.sql)(自声明为全新环境权威初始化入口)
|
||
- 问题:该脚本**没有**创建以下后端实际依赖的表:
|
||
- `crm_crm_expansion` / `crm_crm_expansion_contact`(CRM 拓展主/从表,见 [CrmExpansionMapper.xml](file:///Users/kangwenjing/Downloads/crm/unis_crm/backend/src/main/resources/mapper/expansion/CrmExpansionMapper.xml))
|
||
- `crm_oms_dict_mapping`(OMS 字典映射,见 [CrmOmsDictMappingMapper.xml](file:///Users/kangwenjing/Downloads/crm/unis_crm/backend/src/main/resources/mapper/CrmOmsDictMappingMapper.xml))
|
||
- `crm_channel_expansion_coverage`(覆盖地市从表,见 [ExpansionMapper.xml#L106-L119](file:///Users/kangwenjing/Downloads/crm/unis_crm/backend/src/main/resources/mapper/expansion/ExpansionMapper.xml#L106-L119))
|
||
- 这 4 张表只存在于增量脚本 [20260827.sql#L45、L77、L92、L142](file:///Users/kangwenjing/Downloads/crm/unis_crm/sql/20260827.sql#L45);已核实 8 个启动期 SchemaInitializer **均未补建**([common/](file:///Users/kangwenjing/Downloads/crm/unis_crm/backend/src/main/java/com/unis/crm/common/) 下只有 dashboard/日历/日报提醒/语音/数据权限等表)。
|
||
- 影响:全新库只跑 `init_full_pg17.sql` 时,CRM 拓展模块、商机 OMS 推送/回传、渠道覆盖地市会在运行时抛 `relation does not exist`。生产库(已执行 20260827.sql)不受影响,但属部署链路硬缺口。
|
||
- 建议:在 `init_full_pg17.sql` 中补齐这 4 张表(可直接并入 20260827.sql 的定义)。
|
||
|
||
### 5. OMS 集成回写的阶段/状态校验与新阶段码不一致(残留死代码,不阻断实际同步)
|
||
- 位置:[OpportunityServiceImpl.java#L920-L925](file:///Users/kangwenjing/Downloads/crm/unis_crm/backend/src/main/java/com/unis/crm/service/impl/OpportunityServiceImpl.java#L920-L925)、[OpportunityServiceImpl.java#L944-L974](file:///Users/kangwenjing/Downloads/crm/unis_crm/backend/src/main/java/com/unis/crm/service/impl/OpportunityServiceImpl.java#L944-L974)
|
||
- 已确认(用户核实):OMS 反写只根据 `archived` 字段触发「已签约」,**不下发 `status=won/lost`**。因此 `archived=true → 强制 stage=S5` 路径([L922-L925](file:///Users/kangwenjing/Downloads/crm/unis_crm/backend/src/main/java/com/unis/crm/service/impl/OpportunityServiceImpl.java#L922-L925))实际可正常同步「已签单」,**不构成阻断**。
|
||
- 残留问题(低危):
|
||
- `resolveIntegrationStatus` 仅识别字面量 `"won"`/`"lost"` 阶段,但阶段经 `mapStageToCrm` 映射后为 `S5`/`L`,这两个分支是**死代码**;`WON_STAGE_CODES = Set.of("won","S6")`([L65](file:///Users/kangwenjing/Downloads/crm/unis_crm/backend/src/main/java/com/unis/crm/service/impl/OpportunityServiceImpl.java#L65))定义后从未使用,且 `S6` 实为 OMS 码,佐证旧逻辑未随阶段码迁移同步更新。
|
||
- 若 OMS 未来开始下发 `status=won/lost`,仍会走到 `switch` 的 `case "won","lost"` 抛错;若 OMS 下发 `stage`(如 `L`)但不带 status,商机 `status` 会落为 `active`(语义不符,但 `crm_opportunity.status` 允许 `active`,不报错)。
|
||
- 建议:后续可在 `resolveIntegrationStatus` 中按映射后新码(S5→won、L→lost)推导 status,并清理死代码 `WON_STAGE_CODES`;非阻塞,可留作低优先维护项。
|
||
|
||
---
|
||
|
||
## 二、业务逻辑问题
|
||
|
||
### 6. 打卡可重复提交(当日无去重)
|
||
- 位置:[WorkServiceImpl.java#L324-L355](file:///Users/kangwenjing/Downloads/crm/unis_crm/backend/src/main/java/com/unis/crm/service/impl/WorkServiceImpl.java#L324-L355)
|
||
- 问题:`saveCheckIn` 每次直接 `insertCheckIn`,未先查当日是否已有记录;`updateCheckIn`([WorkMapper.java#L94](file:///Users/kangwenjing/Downloads/crm/unis_crm/backend/src/main/java/com/unis/crm/mapper/WorkMapper.java#L94))为从未调用的死代码;打卡表当日索引非唯一([WorkCheckInSchemaInitializer.java#L37](file:///Users/kangwenjing/Downloads/crm/unis_crm/backend/src/main/java/com/unis/crm/common/WorkCheckInSchemaInitializer.java#L37))。双击/重试会产生多条当日打卡,统计重复。
|
||
|
||
### 7. 日报导出按「业务类型」筛选在 5000 条截断之后执行
|
||
- 位置:[WorkServiceImpl.java#L316-L321](file:///Users/kangwenjing/Downloads/crm/unis_crm/backend/src/main/java/com/unis/crm/service/impl/WorkServiceImpl.java#L316-L321)
|
||
- 问题:`exportDailyReports` 先合并去重并 `limit(EXPORT_LIMIT=5000)`,再用内存 `hasReportLineOfType` 过滤;而打卡导出的 `bizType` 是在 SQL 内过滤([WorkMapper.xml#L35-L37](file:///Users/kangwenjing/Downloads/crm/unis_crm/backend/src/main/resources/mapper/work/WorkMapper.xml#L35-L37))。日期区间大、其它类型行数多时,目标类型日报会被 5000 上限截断,导出结果偏少/为空。
|
||
|
||
### 8. 打卡日期边界使用 DB `current_date`,与日报 Asia/Shanghai 口径不一致
|
||
- 位置:[WorkMapper.xml#L96、L720、L744](file:///Users/kangwenjing/Downloads/crm/unis_crm/backend/src/main/resources/mapper/work/WorkMapper.xml#L96)
|
||
- 问题:打卡按 `checkin_date = current_date`(依赖 DB 会话时区),日报统一用 Asia/Shanghai([WorkServiceImpl.java#L372-L373](file:///Users/kangwenjing/Downloads/crm/unis_crm/backend/src/main/java/com/unis/crm/service/impl/WorkServiceImpl.java#L372-L373))。若 DB 服务器时区非上海,北京时间凌晨 0-8 点打卡的归属日期会错位。
|
||
|
||
### 9. Dashboard「当前阶段」硬编码旧枚举,与商机/拓展新阶段码不一致
|
||
- 位置:[DashboardMapper.xml#L186-L194(商机)、L247-L254(销售)、L307-L314(渠道)](file:///Users/kangwenjing/Downloads/crm/unis_crm/backend/src/main/resources/mapper/dashboard/DashboardMapper.xml#L186-L194)
|
||
- 问题:`case o.stage when 'initial_contact'...'won'/'lost'` 已与新版字典码值(S0/S1/S2/S3/S4/S5/L,见 [20260828update.sql](file:///Users/kangwenjing/Downloads/crm/unis_crm/sql/20260828update.sql))脱节,首页动态会直接显示原始码(如 `S5`、`L`)。建议改为动态 join `sys_dict_item/sj_xmjd`。
|
||
|
||
### 10. 跟进记录新增接口返回的 ID 是「影响行数」而非真实主键
|
||
- 位置:[OpportunityServiceImpl.java#L315-L327](file:///Users/kangwenjing/Downloads/crm/unis_crm/backend/src/main/java/com/unis/crm/service/impl/OpportunityServiceImpl.java#L315-L327)、[ExpansionServiceImpl.java#L497-L506](file:///Users/kangwenjing/Downloads/crm/unis_crm/backend/src/main/java/com/unis/crm/service/impl/ExpansionServiceImpl.java#L497-L506)
|
||
- 问题:`insertOpportunityFollowUp` / `insertExpansionFollowUp` 未配置 `useGeneratedKeys`,`inserted` 恒为 1,接口返回的 `Long` 实际是行数而非跟进记录 ID。前端当前未依赖该 ID,影响较低,但属错误返回值。
|
||
|
||
### 11. 商机导出采用「全量拉取 + 前端过滤」
|
||
- 位置:[Opportunities.tsx#L2255](file:///Users/kangwenjing/Downloads/crm/unis_crm/frontend/src/pages/Opportunities.tsx#L2255)
|
||
- 问题:导出调用 `getOpportunityOverview("", undefined, false, null, ...)`,`limit=null` 一次性拉取租户内全部商机再在客户端过滤。商机规模大时单次请求体量大、耗时长,存在超时/内存风险。建议改为服务端筛选或分批拉取。
|
||
|
||
### 12. 语音识别配置页缺少「未选租户」防护
|
||
- 位置:[speech-recognition-settings/index.tsx#L156、L201-L217](file:///Users/kangwenjing/Downloads/crm/unis_crm/frontend1/src/features/speech-recognition/pages/speech-recognition-settings/index.tsx#L156)
|
||
- 问题:未选租户时保存会落到全局租户(0),静默改写全局配置;而日报提醒页有 `isTenantUnselected` 拦截([report-reminder-settings/index.tsx](file:///Users/kangwenjing/Downloads/crm/unis_crm/frontend1/src/features/report-reminder/pages/report-reminder-settings/index.tsx))。行为不一致。
|
||
|
||
### 13. 前端 `request()` 与 `fetchWithAuth()` 对 401 处理不一致
|
||
- 位置:[auth.ts#L1259-L1271](file:///Users/kangwenjing/Downloads/crm/unis_crm/frontend/src/lib/auth.ts#L1259-L1271) vs [auth.ts#L1298-L1311](file:///Users/kangwenjing/Downloads/crm/unis_crm/frontend/src/lib/auth.ts#L1298-L1311)
|
||
- 问题:`request()` 遇 401 直接 `handleUnauthorizedResponse()`(清登录态跳登录页),不做「刷新+重试」;`fetchWithAuth()` 会先刷新再重试。token 被提前吊销或服务端判定失效但本地未过期时,用户会在操作中被直接登出并丢失已输入内容。
|
||
|
||
### 14. 附件上传走 XHR 但未做 token 主动刷新
|
||
- 位置:[auth.ts#L1668-L1723](file:///Users/kangwenjing/Downloads/crm/unis_crm/frontend/src/lib/auth.ts#L1668-L1723)
|
||
- 问题:`uploadWorkReportAttachment` 直接读 `localStorage.accessToken` 发 XHR,不经过 `ensureFreshAccessToken`。长时间编辑后 token 到期,上传 401 → 直接登出,正在填写的日报/打卡表单数据丢失。建议上传前先刷新 token。
|
||
|
||
---
|
||
|
||
## 三、潜在风险 / 代码规范
|
||
|
||
| # | 问题 | 位置 |
|
||
|---|------|------|
|
||
| 15 | 生产环境 MyBatis `log-impl: StdOutImpl` 打印全部 SQL 与绑定参数(含敏感数据) | [application.yml#L36](file:///Users/kangwenjing/Downloads/crm/unis_crm/backend/src/main/resources/application.yml#L36) |
|
||
| 16 | 语音转写整文件读入内存、无独立大小限制;打卡照片无大小限制(依赖全局 500MB),可致 OOM/存储膨胀 | [WorkServiceImpl.java#L516-L573](file:///Users/kangwenjing/Downloads/crm/unis_crm/backend/src/main/java/com/unis/crm/service/impl/WorkServiceImpl.java#L516-L573)、[WorkServiceImpl.java#L448-L465](file:///Users/kangwenjing/Downloads/crm/unis_crm/backend/src/main/java/com/unis/crm/service/impl/WorkServiceImpl.java#L448-L465) |
|
||
| 17 | 定时任务共享默认单线程调度器,`ReportReminderScheduler`(每分钟)与 `BusinessCalendarAutoSync`(每日 3:15)可能相互阻塞,且无分布式锁 | [ReportReminderScheduler.java](file:///Users/kangwenjing/Downloads/crm/unis_crm/backend/src/main/java/com/unis/crm/service/ReportReminderScheduler.java)、[BusinessCalendarAutoSync.java](file:///Users/kangwenjing/Downloads/crm/unis_crm/backend/src/main/java/com/unis/crm/service/BusinessCalendarAutoSync.java) |
|
||
| 18 | 数据权限完全依赖 unisbase 插件在 SQL 上动态注入,SQL 本身无兜底条件;插件缺失/非 Web 上下文可能返回全库数据 | [WorkMapper.java#L32-L72](file:///Users/kangwenjing/Downloads/crm/unis_crm/backend/src/main/java/com/unis/crm/mapper/WorkMapper.java#L32) |
|
||
| 19 | 前端权限判断 fail-open:权限码列表为空时全部放行(`if (!codes.length) return true`),权限拉取失败时 UI 虚假放行 | [auth.ts#L1469-L1483](file:///Users/kangwenjing/Downloads/crm/unis_crm/frontend/src/lib/auth.ts#L1469-L1483) |
|
||
| 20 | `20260828update.sql` 的 `UPDATE ... FROM crm_oms_dict_mapping` 依赖 `oms_value` 唯一,但表唯一约束为 `(dict_type, crm_value, oms_value)`,未约束 `(dict_type, oms_value)` 唯一,后续新增映射可能产生非确定性更新 | [20260828update.sql#L106-L112](file:///Users/kangwenjing/Downloads/crm/unis_crm/sql/20260828update.sql#L106-L112)、[20260827.sql#L104](file:///Users/kangwenjing/Downloads/crm/unis_crm/sql/20260827.sql#L104) |
|
||
| 21 | 生产 profile 企微默认 `enabled:false`,与 SSO 期望(redirect-uri 指向 crm.unissense.top)不一致;`/api/wecom/sso/**`、`/api/opportunities/integration/**` 为 permit-all | [application-prod.yml#L72-L82](file:///Users/kangwenjing/Downloads/crm/unis_crm/backend/src/main/resources/application-prod.yml#L72-L82) |
|
||
| 22 | frontend1 `http.ts` 鉴权白名单用 `url.includes(path)` 子串匹配,路径匹配过度放宽 | [http.ts#L14、L68-L70](file:///Users/kangwenjing/Downloads/crm/unis_crm/frontend1/src/api/http.ts#L14) |
|
||
| 23 | 生产 MinIO `endpoint` 为 `https://miniodown.nex.unisspace.com` 但 `use_ssl: false`,可能连接异常 | [application-prod.yml#L5-L10](file:///Users/kangwenjing/Downloads/crm/unis_crm/backend/src/main/resources/application-prod.yml#L5-L10) |
|
||
| 24 | 后端 Dockerfile 复制 `target/unis-crm-backend-1.0.0-SNAPSHOT.jar`,但仓库内 jar 位于 `backend/` 根目录(非 target/),依赖 CI 先执行 `mvn package`,否则 `docker compose build` 会失败 | [backend/Dockerfile#L7](file:///Users/kangwenjing/Downloads/crm/unis_crm/backend/Dockerfile#L7) |
|
||
| 25 | 打卡/日报元数据使用字符串内嵌标记(`[[CHECKIN_PHOTOS]]`、`[[WORK_REPORT_LINES]]` 等)拼入 remark 字段,解析依赖精确格式,健壮性差 | [WorkServiceImpl.java#L91-L102](file:///Users/kangwenjing/Downloads/crm/unis_crm/backend/src/main/java/com/unis/crm/service/impl/WorkServiceImpl.java#L91-L102) |
|
||
| 26 | `ActionDialog` 确认回调 `void onConfirm()` 未捕获 rejection | [ActionDialog.tsx#L150](file:///Users/kangwenjing/Downloads/crm/unis_crm/frontend/src/components/ActionDialog.tsx#L150) |
|
||
| 27 | 腾讯地图生产 Key 硬编码进前端源码作兜底 | [tencentMap.ts#L1](file:///Users/kangwenjing/Downloads/crm/unis_crm/frontend/src/lib/tencentMap.ts#L1) |
|
||
| 28 | 移动端多选下拉点击遮罩关闭不「回滚」已勾选项,与常规确定/取消交互不一致,易误改覆盖省市 | [AdaptiveSelect.tsx#L204-L216](file:///Users/kangwenjing/Downloads/crm/unis_crm/frontend/src/components/AdaptiveSelect.tsx#L204-L216) |
|
||
| 29 | 浏览器定位最坏路径约 33s,弱信号下用户易误以为卡死 | [tencentMap.ts#L145-L176](file:///Users/kangwenjing/Downloads/crm/unis_crm/frontend/src/lib/tencentMap.ts#L145-L176) |
|
||
| 30 | Dashboard 权限/消息加载静默吞异常,问题被掩盖 | [DashboardServiceImpl.java#L171-L207](file:///Users/kangwenjing/Downloads/crm/unis_crm/backend/src/main/java/com/unis/crm/service/impl/DashboardServiceImpl.java#L171-L207) |
|
||
|
||
---
|
||
|
||
## 四、已核对且判定为正常的重点项
|
||
|
||
1. **商机 tab 阶段筛选/导出约束(与项目硬约束一致)**
|
||
- 未签单 tab:`excludeStageCodes=lostStageCodes`(排除 L-已丢单)✓([Opportunities.tsx#L2254](file:///Users/kangwenjing/Downloads/crm/unis_crm/frontend/src/pages/Opportunities.tsx#L2254)、后端 [OpportunityMapper.xml#L288-L294](file:///Users/kangwenjing/Downloads/crm/unis_crm/backend/src/main/resources/mapper/opportunity/OpportunityMapper.xml#L288-L294))
|
||
- 已丢单 tab:仅 `stageCodes=lostStageCodes` ✓
|
||
- 切换 tab 重置阶段筛选 ✓([Opportunities.tsx#L1718-L1725](file:///Users/kangwenjing/Downloads/crm/unis_crm/frontend/src/pages/Opportunities.tsx#L1718-L1725))
|
||
- 阶段选项动态取自 `sys_dict_item/sj_xmjd`,未硬编码 ✓
|
||
2. **渠道拓展/CRM 拓展互移流程**:属主校验、字段映射(覆盖省市/办公地址/联系人)、跟进/签到/日报消息的 biz_type 迁移、旧记录清理均正确([ExpansionServiceImpl.java#L398-L495](file:///Users/kangwenjing/Downloads/crm/unis_crm/backend/src/main/java/com/unis/crm/service/impl/ExpansionServiceImpl.java#L398-L495)),与前端弹窗字段、校验一致。
|
||
3. **覆盖地市存储模型**:后端读写 `crm_channel_expansion_coverage` 从表([ExpansionMapper.xml#L106-L119](file:///Users/kangwenjing/Downloads/crm/unis_crm/backend/src/main/resources/mapper/expansion/ExpansionMapper.xml#L106-L119)),`insertChannelExpansion` 不引用废弃的 coverage 列,与 20260827.sql 的设计一致 ✓
|
||
4. **商机集成 archived→S5 强制**:已实现并通过单测([OpportunityServiceImplTest.java#L211-L228](file:///Users/kangwenjing/Downloads/crm/unis_crm/backend/src/test/java/com/unis/crm/service/impl/OpportunityServiceImplTest.java#L211-L228));且经确认 OMS 反写仅用 `archived` 字段触发「已签约」(不下发 `status=won/lost`),实际同步正常 ✓
|
||
5. **SQL 增量脚本幂等性**:`create table if not exists`、`drop constraint if exists`、`on conflict do nothing/update` 均正确;20260828update.sql 的换算用单条 UPDATE 避免连环转换 ✓
|
||
6. **前后端字段一致性**:`MoveCrmToChannelRequest`(coverageProvince/coverageCity/coverageItems)、`CreateOpportunityRequest` 与前端序列化一致;confidencePct 正则与前端兜底一致 ✓
|
||
7. **Nginx 延迟解析**:两个前端 nginx 均使用 `resolver + set $backend_host` 延迟解析域名,符合约束 ✓([default.conf.template#L8、L16-L19](file:///Users/kangwenjing/Downloads/crm/unis_crm/frontend/nginx/default.conf.template#L8))
|
||
8. **前端 Dockerfile/.dockerignore**:`.dockerignore` 已排除 node_modules、.git、系统目录,多阶段构建合理 ✓
|
||
9. **日报保存逻辑**:「当日重复提交→更新」「北京时间 10 点前归属前一天」「提醒在事务提交后触发」均正确([WorkServiceImpl.java#L357-L415](file:///Users/kangwenjing/Downloads/crm/unis_crm/backend/src/main/java/com/unis/crm/service/impl/WorkServiceImpl.java#L357-L415))
|
||
|
||
---
|
||
|
||
## 五、修复优先级建议
|
||
|
||
| 优先级 | 问题编号 | 理由 |
|
||
|--------|---------|------|
|
||
| P0 | #1、#2 | 越权与凭据泄露,安全风险最高 |
|
||
| P0 | #4 | 全新部署直接功能瘫痪 |
|
||
| P1 | #3 | 前端业务阻断,日报当天无法提交 |
|
||
| P2 | #6、#7、#8、#9、#11、#13、#14 | 数据正确性与一致性 |
|
||
| P3 | #5 及其余 | #5 已确认不阻断实际同步(OMS 仅用 archived 反写),降为低危维护项 |
|
||
|
||
> 说明:本报告仅做静态审计,未运行代码;部分结论(如 #5 是否实际触发、#11 的量级、#8 的时区)建议结合运行时数据/日志复核后再修复。
|