Browse Source

merge: resolve conflict in crm-app.log

master
luoweijian 4 hours ago
parent
commit
57d554141b
  1. 57
      .scratch/a7-heavy/demo-smoke-result.json
  2. 3
      .scratch/a7-heavy/demo/index.html
  3. 10
      .scratch/a7-heavy/demo_smoke.py
  4. 32
      .scratch/a7-heavy/issues/04-bruno-regen-baseline.md
  5. 28
      .scratch/a7-heavy/issues/05-e2e-demo-smoke.md
  6. 3
      .scratch/a7-heavy/map.md
  7. 84
      .scratch/a7-heavy/probe_once.py
  8. BIN
      .scratch/a7-heavy/shots/03-sync-stats.png
  9. BIN
      .scratch/a7-heavy/shots/04-glog-prepare.png
  10. BIN
      .scratch/a7-heavy/shots/05-glog-export.png
  11. BIN
      .scratch/a7-heavy/shots/06-glog-denied.png
  12. BIN
      .scratch/a7-heavy/shots/07-poolrule-base.png
  13. BIN
      .scratch/a7-heavy/shots/08-cleanup.png
  14. 10
      .scratch/a7-parity/demo_smoke.py
  15. 1
      .scratch/a7-parity/gap-report.md
  16. 4
      .scratch/a7-parity/map.md
  17. 13
      .scratch/customer-contact-graph/issues/00-prototype-demo.md
  18. 1243
      .scratch/customer-contact-graph/prototype/prototype-contact-graph.html
  19. 176
      .scratch/customer-rework/bruno-recon-d.json
  20. 2
      .scratch/customer-rework/bruno_scan_d.py
  21. 2250
      logs/crm-app.log
  22. 1643
      logs/mvn-pkg-a7h05.log
  23. 3732
      logs/mvn-test-a7h04.log

57
.scratch/a7-heavy/demo-smoke-result.json

@ -1,13 +1,17 @@
{
"ts": "172728",
"ts": "214637",
"console_errors": [
"Access to fetch at 'http://localhost:8080/api/log/batch/download?ids=752581017469976576&ids=752581021928521728' from origin 'null' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.",
"Failed to load resource: net::ERR_FAILED",
"Failed to load resource: the server responded with a status of 403 ()",
"Failed to load resource: the server responded with a status of 403 ()",
"Failed to load resource: the server responded with a status of 403 ()"
],
"page_errors": [],
"exempt_403": [
"Failed to load resource: the server responded with a status of 403 ()",
"Failed to load resource: the server responded with a status of 403 ()",
"Failed to load resource: the server responded with a status of 403 ()"
],
"real_console_errors": [],
"results": [
{
"step": "登录 debug/token + /auth/me",
@ -57,22 +61,22 @@
{
"step": "AOP 写端点采集素材(建/删演示角色)",
"ok": true,
"note": "写操作完成:建/删演示角色 e2d-日志演示角色-172728(等待采集落库后断言)"
"note": "写操作完成:建/删演示角色 e2d-日志演示角色-214637(等待采集落库后断言)"
},
{
"step": "操作日志分页(新增/删除行命中 + 时间范围逐字段)",
"ok": false,
"note": "失败: AOP 采集行未命中: 新增=false 删除=true"
"ok": true,
"note": "AOP 采集行命中:新增行 content=/api/roles/saveOrUpdate,操作人=罗伟健,结果=成功 逐字段:今日范围 10 行 / 2000-01-01 范围 0 行(时间过滤生效)"
},
{
"step": "登录日志「退出」行(内联埋点 + token 重铸)",
"ok": false,
"note": "失败: 登录日志「退出」行未命中(operatorName=罗伟健)"
"ok": true,
"note": "退出埋点命中:opType=退出,operatorName=罗伟健,status/result=成功,ip=0:0:0:0:0:0:0:1,opTime=2026-09-08 21:46:46 (to"
},
{
"step": "批量日志「导出」行(result+fileId+ZIP 魔数)",
"ok": true,
"note": "导出留档行命中:page=线索设置,result=成功,countDetail=成功导出 2 条公海池,fileId=752581016106827776 (导出字节 4204,ZIP 魔数 ✓)"
"note": "导出留档行命中:page=线索设置,result=成功,countDetail=成功导出 2 条公海池,fileId=752646072127455232 (导出字节 4205,ZIP 魔数 ✓)"
},
{
"step": "批量日志「导入」行(BatchResult 计数)",
@ -81,18 +85,18 @@
},
{
"step": "结果文件下载 /api/log/batch/file(ZIP 魔数)",
"ok": false,
"note": "失败: 结果文件非 ZIP(xlsx):102 字节"
"ok": true,
"note": "结果文件下载成功:日志行 id=752646072353947648(fileId=752646072127455232),4205 字节,ZIP 魔数 PK ✓(90 天内有效)"
},
{
"step": "打包下载 /api/log/batch/download ids[](ZIP 魔数)",
"ok": false,
"note": "失败: Failed to fetch"
"ok": true,
"note": "打包下载成功:ids=[752646072353947648, 752646076426616832],3660 字节,ZIP 魔数 PK ✓"
},
{
"step": "不存在文件 id 错误形态(40401/66001/404)",
"ok": false,
"note": "失败: 不存在的文件 id 竟返回 96 字节——未被拦截"
"ok": true,
"note": "不存在文件 id 错误形态生效:code=40401,批量操作日志不存在: 999999999999 (查无日志行→40401/HTTP404;过期→66001;行存在但无文件→66002;90 天分"
},
{
"step": "BUDDY 三 page 权限拒绝(ADMIN 门禁)",
@ -101,18 +105,18 @@
},
{
"step": "公海规则发布(部门专用)+ boundOppCount 基线",
"ok": false,
"note": "失败: 无可用发布中部门专用规则(含本部门)"
"ok": true,
"note": "规则就绪:id=752646085117214720,name=e2d-绑定数规则-214637,scope=2(部门专用),deptIds=[744841292348915712] boundOppCount 基线 base=16(本轮新建发布) 后续按增量"
},
{
"step": "商机 A → boundOppCount=base+1(match 语义)",
"ok": false,
"note": "失败: 先执行步骤1"
"ok": true,
"note": "商机 A(id=752646090414620672)→ boundOppCount=17(base+1 ✓,match 语义生效)"
},
{
"step": "商机 B → boundOppCount=base+2(动态统计非快照)",
"ok": false,
"note": "失败: 先执行步骤1"
"ok": true,
"note": "商机 B(id=752646094093025280)→ boundOppCount=18(base+2 ✓,动态统计非快照)"
},
{
"step": "默认规则兜底口径(无默认规则降级提示)",
@ -122,13 +126,18 @@
{
"step": "清理(商机 close 正门 + 规则停用)",
"ok": true,
"note": "清理完成:关停演示商机 0/0(close 终态;已关闭仍计数,仅 opp_status=5 排除——增量口径跨轮安全);无本轮规则"
"note": "清理完成:关停演示商机 2/2(close 终态;已关闭仍计数,仅 opp_status=5 排除——增量口径跨轮安全);演示规则已停用(发布行留存可辨)"
},
{
"step": "网络层零旧契约形态",
"ok": true,
"note": "bad_method=[] old_style=[] (共55请求)"
"note": "bad_method=[] old_style=[] (共47请求)"
},
{
"step": "console/page error 双零(预期 403 拒绝豁免)",
"ok": true,
"note": "raw_console=3 exempt_403=3 real=0 page_errors=0"
}
],
"all_pass": false
"all_pass": true
}

3
.scratch/a7-heavy/demo/index.html

@ -530,7 +530,8 @@ async function logPackDownload() {
body: ids.join('&') });
if (!r.ok) throw new Error('HTTP ' + r.status);
const buf = new Uint8Array(await r.arrayBuffer());
if (!isZip(buf)) { failBox('glogOut', new Error('打包下载非 ZIP: ' + buf.length + ' 字节')); return; }
// isZip 之外加字节数下限:空 ZIP(仅 end-of-central-dir)恰为 22 字节且魔数合法,文件全不可取时会误 PASS
if (!isZip(buf) || buf.length < 100) { failBox('glogOut', new Error('打包下载非 ZIP 或空包: ' + buf.length + ' 字节')); return; }
okBox('glogOut', '打包下载成功:ids=[' + ids.map(s => s.replace('ids=', '')).join(', ') + '],' + buf.length + ' 字节,ZIP 魔数 PK ✓');
} catch (e) { failBox('glogOut', e); }
}

10
.scratch/a7-heavy/demo_smoke.py

@ -10,14 +10,14 @@
任务行与登录/操作/批量日志行 = 审计数据留存不删同步设置由 demo 步骤7 自还原原值
商机 close 正门终态已关闭仍计数 增量断言 base+n 跨轮安全
"""
import json, re, time, pathlib, sys, io
import json, os, re, time, pathlib, sys, io
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
from playwright.sync_api import sync_playwright
ROOT = pathlib.Path(__file__).resolve().parent
SHOTS = ROOT / "shots"; SHOTS.mkdir(exist_ok=True)
DEMO = ROOT / "demo" / "index.html"
API = "http://localhost:8080"
SHOTS = pathlib.Path(os.environ.get("E2D_SHOTS") or (ROOT / "shots")); SHOTS.mkdir(exist_ok=True)
DEMO = pathlib.Path(os.environ.get("E2D_DEMO") or (ROOT / "demo" / "index.html"))
API = os.environ.get("E2D_API") or "http://localhost:8080"
ADMIN = "739564171091247104"
BUDDY = "744842318024015872"
TS = time.strftime("%H%M%S")
@ -270,7 +270,7 @@ summary = {"ts": TS, "console_errors": console_errors, "page_errors": page_error
"exempt_403": expected_403, "real_console_errors": real_console,
"results": results,
"all_pass": all(r["ok"] for r in results) and not real_console and not page_errors}
(ROOT / "demo-smoke-result.json").write_text(json.dumps(summary, ensure_ascii=False, indent=1), encoding="utf-8")
pathlib.Path(os.environ.get("E2D_RESULT") or (ROOT / "demo-smoke-result.json")).write_text(json.dumps(summary, ensure_ascii=False, indent=1), encoding="utf-8")
print("\n== SUMMARY ==")
for r in results:
print(("PASS " if r["ok"] else "FAIL ") + r["step"] + (" | " + r["note"] if r["note"] else ""))

32
.scratch/a7-heavy/issues/04-bruno-regen-baseline.md

@ -1,9 +1,12 @@
# 04 bruno 重生成 + 基线验收收官
Type: task
Status: open
Status: resolved
Blocked by: 01, 02, 03, 05
> Claimed 2026-09-08 收官会话,同日 resolved(见下 Answer)。
> 编排说明:e2e(票 05)前置于本收官票——一期经验(票 13 在收官票 12 之后发现 D-01 代码缺陷与 F-01 文档缺陷,修复穿透了收官验收);二期把 e2e 修复闭环后再做终态基线验收,收官验收覆盖修复后代码。
## Question
@ -14,3 +17,30 @@ Blocked by: 01, 02, 03, 05
2. **全仓基线验收**:*.java BOM 扫描 0 + mvn compile 过 + 全仓单测全绿(9 模块 + 新 crm-log = 10;注意跨模块集成测试内嵌 H2 DDL 是否需补新表列——一期票 12 有同款漏网先例)。
3. **记账清单终态标注**:a7-parity [gap-report.md](../../a7-parity/gap-report.md) §六「已记账」逐项标注执行结果(同步任务/全局日志/公海绑定数→已执行;数据共享 3 页→仍挂账等原型正式化);两 map 交叉链接闭合。
4. 未提交任何 git(后端仓 + 文档仓均留用户审查)。
## Answer
2026-09-08 收官执行完毕,验收全过:
### 1. bruno 对账双向零差(文档仓 E:\code\crm-api-docs,改动未提交 git)
- 对账通道 bruno_scan_d.py(customer-rework 期脚本复用)ROOTS 本票补 `crm-log/src/main/java`——票 02 只改了 bruno-sync.config.json,对账脚本滞后,不补则 crm-log 5 端点误报 delete。
- 首跑 381=381 但 1 new / 1 delete:「同步任务详情.bru」docs key 写成 `GET /api/system/sync/tasks/detail?id=`(票 01 落文件笔误,全仓 381 份孤例,其余 detail 类 key 均干净);修 key 去 `?id=` 后复跑:**source_key_count 381 = generated_file_count 381,keep 381 / new 0 / delete 0 / unrecognized 0 / conflicts 0 / private 0——双向零差**。数字闭环:381 = 一期终态 372 + 同步任务 5(票 01)+ 日志 5(票 02)− 旧 POST /sync/dingtalk key(任务化后消失)。
- 本票无新增 .bru:票 01/02 的 10 份 + 票 03 的 1 处 patch 已各自落位,收官对账即验收。
### 2. 基线验收
- 全仓 *.java BOM 扫描 0(.scratch/bom-scan.ps1)。
- mvn test 全链路(test 阶段蕴含 compile)BUILD SUCCESS 1:22 min:12 reactor 全 SUCCESS,10 测试模块 **1290 用例 0 失败**(base 44 / file 84 / auth 241 / dict 63 / rule 218 / preference 23 / customer 131 / lead 186 / opportunity 266 / log 34)。
- 票面警示的「跨模块集成测试内嵌 H2 DDL 漏网」(一期票 12 同款)未复现:crm-log 三表与 dt_sync_task 无跨模块测试引用。
- 环境事实:跑测前停掉票 05 遗留起服(PID 7764)释放内存,防 C2 原生 OOM 重演;MAVEN_OPTS 限堆 + TieredStopAtLevel=1 通道沿用票 05。
### 3. 记账清单终态标注与交叉链接
- gap-report §六追加二期收官标注:原「已记账」三项(同步任务 / 全局日志 / 公海绑定数)转**已执行**,数据共享 3 页仍挂账等原型正式化。
- 两 map 交叉链接闭合:a7-parity map 尾注更新(执行票 01–05 + 二期收官段);a7-heavy map Decisions so far 追加本票行并宣告本 effort 收官。
### 4. git 状态
- 后端仓:本票零源码改动,仅 .scratch(对账脚本 ROOTS 一行 + 本票脚手架);全部未提交留用户审查。
- 文档仓:「同步任务详情.bru」docs key 修正 1 处;未提交留用户审查。

28
.scratch/a7-heavy/issues/05-e2e-demo-smoke.md

@ -1,9 +1,12 @@
# 05 a7-heavy 补码成果 Demo E2E(浏览器级冒烟)
Type: task
Status: claimed
Status: resolved
Blocked by: 01, 02, 03
> Claimed 2026-09-08 wayfinder session(handoff-a7-heavy-05 换机续接收尾),同日 resolved(见下 Answer)。
## Question
参照一期 [13-e2e-demo-smoke](../../a7-parity/issues/13-e2e-demo-smoke.md) 模式,对票 01–03 落地的二期补码成果做浏览器级 e2e 验证。底座**复用 a7-parity demo**(CSS / uid 直登 / toast / 弹窗 / api helper / 步骤面板 JS 模式,沿用 v2 修订后形态——`outBox/logLine` 助手收裸 id 走 `document.getElementById`),**新建** `.scratch/a7-heavy/demo/index.html`(不改动一期 demo),tab 仅覆盖二期三域;TS 经 URL `?ts=` 注入对齐脚本侧。
@ -31,3 +34,26 @@ Blocked by: 01, 02, 03
运行环境:同一期(verify profile + MinIO env 起服 fat jar,完整命令见一期 `demo_smoke.py` 头注)。
验收基线:全 PASS 两轮幂等重跑 + console/page error 双零 + 网络层零旧契约形态(零 PUT/DELETE、零路径变量)。
## Answer
2026-09-08 执行落地。22 步浏览器级冒烟最终**两轮 ALL_PASS**,验收基线全部达成(全 PASS 两轮幂等 + console/page error 双零 + 网络层零旧契约形态:47 请求零 PUT/DELETE、零路径变量)。
**交付物**:`.scratch/a7-heavy/demo/index.html`(3 tab:同步任务/全局日志/公海绑定数,复用一期 demo 底座)+ `demo_smoke.py`(22 步 + pre_clean 幂等 + 截图 shots/ + 结果落 demo-smoke-result.json)。
**轮次轨迹**:首轮 13 PASS / 9 FAIL → 探针定位 5 根因全修 → 21 PASS / 1 FAIL(暴露环境根因,见下)→ 注入 MinIO 凭据 + demo 断言收紧 → 22/22 两轮 ALL_PASS。
**缺陷修复 5 根因(探针轮)**:
1. 源码:`OpLogAspect.resolveOpType` 补 `saveorupdate` 特判(必须先于 update 子串判定),`/api/roles/saveOrUpdate` 由误采「修改」纠正为「新增」。
2. demo:登录日志行断言字段 result → status;`batch/file?id=` 收日志行主键(requireBatchLog→row.fileId,非 fileId 本身);`batch/download` 改 POST(GET 405 且无 CORS 头);`opp-pool-rule/publish` 四旗标全必传(补 recycleRemindEnabled=0,缺则 64008)。
3. smoke:BUDDY 403 的浏览器 console error("status of 403")按预期拒绝豁免,豁免后仍要求真零。
**收尾会话(handoff-a7-heavy-05 换机续接)两段新事实**:
1. **fat jar 重打**:旧 `.jar.original`(9/6 构建)挡 repackage rename → 删除后重打;首次构建遇 JVM C2 原生 OOM(`Chunk::new`,机器 15.4G 物理内存当时仅余 0.4G)→ `MAVEN_OPTS=-Xmx512m -XX:TieredStopAtLevel=1` 关 C2 限堆重试,BUILD SUCCESS 56s,jar 时间戳确认更新(规避增量静默跳过坑)。
2. **第 6 步复跑仍 FAIL(91 字节非 ZIP)→ 根因在环境非代码**:本机起服从未注入 MinIO 凭据(application.yml 兜底 minioadmin ≠ 远程真凭据,启动横幅无报错、直到文件类操作才炸),留档结果文件上传/下载全挂——连带发现第 7 步「打包下载 22 字节 PASS」实为**空 ZIP 误报**(仅 end-of-central-dir 恰 22 字节且魔数合法)。对策:起服注入 `CRM_MINIO_AK` / `CRM_MINIO_SK`(凭据用户实锤,见 customer-e2e 换机环境事实 memory,不落任何仓库文件);demo 第 7 步断言收紧为 `isZip + ≥100 字节`。修复后导出留档真实落库(fileId 对应 4205 字节 ZIP)、单文件下载 4205 字节、打包下载 3661 字节;两轮 ALL_PASS(boundOppCount base 12→14→16,增量口径跨轮安全;pre_clean 每轮清上轮 e2d-导入池;SYNC-0908-01 运行中任务复用走 61023 态,两态容忍设计如约生效)。
**清理**:`probe_once.py` 已删;`/api/roles/page?roleName=e2d-probe` 无残留(角色表仅内置「管理员」)。
**换机后本机环境事实(沉淀)**:构建通道 = IDEA bundled maven + JBR(PATH 无 mvn/java);smoke 解释器 = `.scratch/customer-e2e/.venv10` 的 python -X utf8(playwright + openpyxl,chromium-1234 走默认缓存 `%LOCALAPPDATA%\ms-playwright`);**本机起服完整前提 = verify profile + MinIO AK/SK 环境变量**——此为本票新增硬前提,缺则票 02 全部文件类断言(file/download/ZIP 魔数)必挂且失败形态是 91 字节 JSON 错误信封,极易误判为 demo 缺陷。

3
.scratch/a7-heavy/map.md

@ -25,6 +25,9 @@ Label: wayfinder:map · charted 2026-09-08
- 2026-09-08 票 01 执行落地(resolved):同步任务 5 端点任务化全量实现——契约改型返 `DtSyncLaunchDTO{taskId, taskNo}`、launch 并发占位 61023、单线程池异步、进度 5/45/90/100;**组织拉取抽离 `OrgSourcePort` SPI(执行中用户拍板)**:`DingTalkOrgSource` 实现 + 装配缝 `crm.auth.org-source`(缺省 dingtalk),编排层 `DtOrgSyncServiceImpl` 平台无关化零 SDK 依赖,后续企微/自有 OA 新增实现类即可;lastSyncTime 直接切换 dt_sync_task 最新成功行(Redis 兼容读取未做);重试规则仅落设置+边界校验不实现自动重试;partial_failed 枚举保留但不产生;错误码 61022-61024、权限种子 @Order(25)、bruno 5 份落文档仓;验证 crm-auth 全量单测 EXIT=0 + BOM 0。
- 2026-09-08 票 02 执行落地(resolved):**全局日志 crm-log 新模块**(轻模块不并入 crm-auth),三表 sys_log_operation/login/batch;**采集机制拍板①混合**:AOP 注解切面(全仓首个 @Aspect,补 spring-boot-starter-aop)兑底全 Controller POST + crm-base `OpLogPort` SPI 内联埋点(crm-auth 登录/退出 5 钩子 + crm-rule LeadPool 批量 3 处,ObjectProvider 容忍缺 Bean,依赖方向零循环);**覆盖面拍板②写侧先行**:不采「查询」GET(量化 81 GET/188 POST),op_type 8 枚举保留定义;批量结果文件 collector 统一落盘 + 90 天保留(下载校验 + cleanup task 3:30 错峰);权限种子 @Order(26);**错误码取 66xxx 段(非票面 65xxx——65001~65999 已被 crm-lead 占用,README 已登记)**;bruno 文档 5 份落文档仓;验证编译过 + BOM 0 + crm-log 34 用例 + 全仓回归 1273 用例全绿。
- 2026-09-08 票 [03](issues/03-pool-rule-bound-count.md) 执行落地(resolved):方案 B detail 动态统计零 DDL;**端口落位偏差**——计数 port 归 `com.crm.rule.port.outbound.PoolRuleOpportunityCountPort`(票面 crm-base 偏差,依 ADR-0025 同消费方同方向先例,实现归 crm-opportunity adapter,零 pom 改动,[ADR-0035](../docs/adr/0035-pool-rule-bound-count-port-seam.md));**page 不补列**(票面「原型列头在列表页」前提被 v29 推翻,仅 detail 回填);口径三定:公海态照常计入(owner_dept_id 保留即 match 输入)/ 排除已转项目 opp_status=5 / port 缺失 fail-open 按 0 展示;默认规则 = 不命中发布中专用规则的商机数(排除集 = 发布中专用规则部门并集);detail 出参 `boundOppCount`(Long→string 序列化);bruno 仅 patch 公海规则详情.bru 结构表一行(实测示例 JSON 不动;分页不补——BaseDTO NON_NULL 下 page 无此键,结构表补行反而撒谎);验证 mvn EXIT=0(crm-rule 32 + crm-opportunity 4 用例全绿)+ BOM 0。
- 2026-09-08 票 [05](issues/05-e2e-demo-smoke.md) 执行落地(resolved):demo/index.html(3 tab 二期三域,复用一期底座)+ demo_smoke.py 22 步**两轮 ALL_PASS**;探针修 5 根因(OpLogAspect saveorupdate 特判先于 update 判定 + demo 4 处 + smoke BUDDY 403 豁免);**新增硬环境事实:本机起服必须注入 CRM_MINIO_AK/SK**——application.yml 兜底 minioadmin 连不通远程 MinIO,留档上传/下载全挂,且打包下载会以 22 字节空 ZIP 误 PASS(demo 断言已收紧 isZip + ≥100B);fat jar 重打遇 C2 原生 OOM(内存压力),MAVEN_OPTS 限堆 + TieredStopAtLevel=1 解;probe_once.py 已删、e2d-probe 角色无残留。
- 2026-09-08 票 [04](issues/04-bruno-regen-baseline.md) 执行落地(resolved):**本 effort 收官**。bruno 对账双向零差——bruno_scan_d.py ROOTS 本票补 crm-log(对账脚本滞后于 bruno-sync.config.json);修同步任务详情.bru docs key 多 `?id=` 后缀孤例笔误后 source 381 = generated 381(keep/new/delete/unrecognized/conflicts 全 0;381 = 一期终态 372 + 同步任务 5(票 01)+ 日志 5(票 02)− 旧 POST /sync/dingtalk key 任务化消失)。基线验收:BOM 0 + mvn test 全链路 BUILD SUCCESS(12 reactor 全 SUCCESS,10 测试模块 1290 用例 0 失败:base 44 / file 84 / auth 241 / dict 63 / rule 218 / preference 23 / customer 131 / lead 186 / opportunity 266 / log 34;跑前停服防 C2 原生 OOM 重演,MAVEN_OPTS 限堆通道沿用);票面警示的跨模块 H2 DDL 漏网未复现。记账标注:gap-report §六 三项转已执行 + 两 map 交叉链接闭合。执行票 01–05 全 resolved;后端仓(仅 .scratch)与文档仓(1 处 key 修正)均未提交 git,留用户审查。
## Not yet specified

84
.scratch/a7-heavy/probe_once.py

@ -1,84 +0,0 @@
# -*- coding: utf-8 -*-
"""票05 首轮 FAIL 根因探针(一次性,验证后删):operation/login/batch 日志真实行 + batch/file·download 正确姿势 + publish 被拒原因"""
import json, urllib.request, urllib.parse, urllib.error, sys, io
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
API = "http://localhost:8080"
ADMIN = "739564171091247104"
T = json.loads(urllib.request.urlopen(f"{API}/api/auth/debug/token?userId={ADMIN}", timeout=15).read().decode())["data"]
def post(path, data=None):
req = urllib.request.Request(API + path, urllib.parse.urlencode(data or {}).encode(), method="POST")
req.add_header("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8")
req.add_header("Authorization", "Bearer " + T)
try:
with urllib.request.urlopen(req, timeout=30) as r:
return json.loads(r.read().decode())
except urllib.error.HTTPError as e:
return {"code": -1, "message": f"HTTP {e.code}"}
def raw(path, method="GET", data=None, form_pairs=None):
body = None
if form_pairs:
body = urllib.parse.urlencode(form_pairs).encode()
elif data:
body = urllib.parse.urlencode(data).encode()
req = urllib.request.Request(API + path, body, method=method)
if body: req.add_header("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8")
req.add_header("Authorization", "Bearer " + T)
try:
with urllib.request.urlopen(req, timeout=30) as r:
b = r.read()
return {"status": r.status, "len": len(b), "head": b[:120], "magic": b[:2]}
except urllib.error.HTTPError as e:
return {"status": e.code, "len": -1, "head": e.read()[:120]}
print("== 1. operation/page 最近 30 行(无筛选,看 opType/operatorName/content 真实值)==")
d = post("/api/log/operation/page", {"current": 1, "size": 30})
for r in (d.get("data") or {}).get("content", [])[:30]:
print(json.dumps(r, ensure_ascii=False)[:230])
print("== 2. login/page 最近 15 行(找「退出」行)==")
d = post("/api/log/login/page", {"current": 1, "size": 15})
for r in (d.get("data") or {}).get("content", [])[:15]:
print(json.dumps(r, ensure_ascii=False)[:230])
print("== 3. batch/page 最近 5 行(rowId vs fileId)==")
d = post("/api/log/batch/page", {"current": 1, "size": 5})
rows = (d.get("data") or {}).get("content", [])
for r in rows:
print(json.dumps(r, ensure_ascii=False)[:230])
if rows:
rid = rows[0]["id"]
print(f"== 4. GET batch/file?id={rid}(传日志行主键)==")
print(raw(f"/api/log/batch/file?id={rid}"))
print(f"== 5. POST batch/download ids=[{rid}](单 id)==")
print(raw("/api/log/batch/download", method="POST", form_pairs=[("ids", str(rid))]))
if len(rows) >= 2:
print("== 5b. POST batch/download ids=[两行] ==")
print(raw("/api/log/batch/download", method="POST",
form_pairs=[("ids", str(rows[0]["id"])), ("ids", str(rows[1]["id"]))]))
print("== 6. publish 试探(e2d-绑定数规则-probe,部门专用 admin 部门)==")
d = post("/api/rule/opp-pool-rule/publish", {
"ruleName": "e2d-绑定数规则-probe", "applyScope": 2, "deptIds": "744841292348915712",
"isDefault": 0, "allowManualPool": 1, "autoRecycleEnabled": 0, "allowFreeClaim": 1,
"ruleDesc": "probe 临时"})
print(json.dumps(d, ensure_ascii=False)[:400])
print("== 7. opp-pool-rule/page 最近 20 行(看占用与状态分布)==")
d = post("/api/rule/opp-pool-rule/page", {"current": 1, "size": 20})
for r in (d.get("data") or {}).get("content", [])[:20]:
print(json.dumps(r, ensure_ascii=False)[:200])
print("== 8. saveOrUpdate 角色后立即查「修改」行(验证 resolveOpType 顺序 bug)==")
d = post("/api/roles/saveOrUpdate", {"roleName": "e2d-probe角色-x", "roleCode": "e2d_probe_x", "remark": "probe"})
print("saveOrUpdate:", json.dumps(d, ensure_ascii=False)[:120])
d = post("/api/log/operation/page", {"current": 1, "size": 5})
for r in (d.get("data") or {}).get("content", [])[:5]:
print(json.dumps(r, ensure_ascii=False)[:200])
# 清理 probe 角色
d = post("/api/roles/page", {"current": 1, "size": 10, "roleName": "e2d-probe角色-x"})
for r in (d.get("data") or {}).get("content", []):
print("cleanup role:", post("/api/roles/delete", {"id": str(r["id"])}).get("code"))

BIN
.scratch/a7-heavy/shots/03-sync-stats.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 72 KiB

BIN
.scratch/a7-heavy/shots/04-glog-prepare.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 77 KiB

BIN
.scratch/a7-heavy/shots/05-glog-export.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 76 KiB

BIN
.scratch/a7-heavy/shots/06-glog-denied.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 76 KiB

BIN
.scratch/a7-heavy/shots/07-poolrule-base.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 76 KiB

BIN
.scratch/a7-heavy/shots/08-cleanup.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 72 KiB

10
.scratch/a7-parity/demo_smoke.py

@ -10,14 +10,14 @@
附带断言: 网络层零 PUT/DELETE /api/<module>/<digits>/ 路径变量console/page error
清理: 演示角色/权限点/规则草稿/公海池 API 正门删除BUDDY 角色还原移交一来一回净零
"""
import json, re, time, pathlib, sys, io
import json, os, re, time, pathlib, sys, io
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
from playwright.sync_api import sync_playwright
ROOT = pathlib.Path(__file__).resolve().parent
SHOTS = ROOT / "shots"; SHOTS.mkdir(exist_ok=True)
DEMO = ROOT / "demo" / "index.html"
API = "http://localhost:8080"
SHOTS = pathlib.Path(os.environ.get("E2D_SHOTS") or (ROOT / "shots")); SHOTS.mkdir(exist_ok=True)
DEMO = pathlib.Path(os.environ.get("E2D_DEMO") or (ROOT / "demo" / "index.html"))
API = os.environ.get("E2D_API") or "http://localhost:8080"
ADMIN = "739564171091247104"
BUDDY = "744842318024015872"
TS = time.strftime("%H%M%S")
@ -474,7 +474,7 @@ xlsx.unlink(missing_ok=True)
summary = {"ts": TS, "console_errors": console_errors, "page_errors": page_errors,
"results": results,
"all_pass": all(r["ok"] for r in results) and not console_errors and not page_errors}
(ROOT / "demo-smoke-result.json").write_text(json.dumps(summary, ensure_ascii=False, indent=1), encoding="utf-8")
pathlib.Path(os.environ.get("E2D_RESULT") or (ROOT / "demo-smoke-result.json")).write_text(json.dumps(summary, ensure_ascii=False, indent=1), encoding="utf-8")
print("\n== SUMMARY ==")
for r in results:
print(("PASS " if r["ok"] else "FAIL ") + r["step"] + (" | " + r["note"] if r["note"] else ""))

1
.scratch/a7-parity/gap-report.md

@ -66,3 +66,4 @@
- **已执行**:缺接口 9 组中 7 组落地(用户管理 4 / 角色 4 / 权限点 3 / 推送规则 8 / 查重规则 8 / 项目规则 6 / LeadPool 1 字段+2 端点);缺入出参全落(UserPageParam×4 / UserStats 时间戳×3 / RoleParam×2 / RoleDTO×3 / 阶段模板条件式档);语义差异按票 06 拍板兑现(资源删除级联→禁删等);tag 12 处全修;bruno-sync 重生成对账双向零差(372=372)。
- **已记账(本期不做)**:同步任务 5 端点(钉钉任务调度对接);全局日志 5 端点+新全局表+AOP+批量结果文件 90 天管理(归属新建 crm-log);公海规则 detail「绑定商机数量」出参(P2,依赖商机侧引用字段);A7-2-3 数据共享 3 页(等原型正式化)。
- **2026-09-08 更新**:前三项已毕业为二期 effort [a7-heavy](../a7-heavy/map.md)(建票 01 同步任务 / 02 全局日志 crm-log / 03 公海绑定数 / 04 收官对账)。
- **2026-09-08 二期收官标注**:[a7-heavy](../a7-heavy/map.md) 执行票 01–05 全 resolved(收官票 [04](../a7-heavy/issues/04-bruno-regen-baseline.md)),原「已记账」三项全部转**已执行**——同步任务 5 端点(任务化 + 定时调度 + OrgSourcePort SPI,票 01)、全局日志 5 端点 + crm-log 新模块 + AOP 兜底 + OpLogPort 埋点 + 批量结果文件 90 天管理(票 02)、公海规则 detail `boundOppCount` 动态统计(票 03,PoolRuleOpportunityCountPort,ADR-0035);e2e 票 05 两轮 22/22 ALL_PASS 前置于收官。终态基线:bruno 对账双向零差(381=381)、全仓 10 模块 1290 测试 0 失败、BOM 0。**数据共享 3 页仍挂账等原型正式化**(唯一遗留记账项)。

4
.scratch/a7-parity/map.md

@ -53,4 +53,6 @@ A7 后台管理全分区(正式树 31 页:A7-1 用户管理 / A7-2 权限管
- swagger UI 美化、sys_menu 种子菜单数据迁移(ADR-0024 既有遗留)。
- 原型中非 A7 空缺模块(A1X 工作计划、A6 数据看板等)的接口实现。
> **2026-09-08 二期建票**:票 12 记账清单中「同步任务 5 端点 / 全局日志 5 端点 + AOP + 批量结果文件 90 天管理 / 公海规则绑定商机数量」三项毕业为二期 effort [a7-heavy](../a7-heavy/map.md)(执行票 01–04);「数据共享 3 页」仍挂账等原型正式化。
> **2026-09-08 二期建票**:票 12 记账清单中「同步任务 5 端点 / 全局日志 5 端点 + AOP + 批量结果文件 90 天管理 / 公海规则绑定商机数量」三项毕业为二期 effort [a7-heavy](../a7-heavy/map.md)(执行票 01–05,e2e 票 05 后补毕业并前置于收官);「数据共享 3 页」仍挂账等原型正式化。
>
> **2026-09-08 二期收官**:a7-heavy 执行票 01–05 全 resolved——bruno 对账双向零差(381=381)、全仓 10 模块 1290 测试 0 失败、BOM 0;缺口终态标注见 [gap-report §六](gap-report.md)。

13
.scratch/customer-contact-graph/issues/00-prototype-demo.md

@ -29,3 +29,16 @@
- UI.md 3-变体默认不适用:设计权威=Lanhu 原型(原型第一),唯一自由度(SVG vs DOM 渲染、自动布局规则)做成页内可见可挑战的规则面板。
- 原型自身不一致处(列表「共 5 位」vs 图谱 9 卡片、列表江小白=人事经理 vs 图谱=投标商务·其他)在 demo 页脚记录,以图谱侧为权威。
## 实现与自检记录(2026-09-09 续接会话)
- **已落盘**:`prototype/prototype-contact-graph.html` 单文件 1334 行(head+CSS 185 + body/JS 1149),零后端依赖;body/JS 按 handoff §3 定稿重建(AGENTS.md 降级链:分块写 → python 合并 → 回读验证)。
- **实现范围**:图谱查看态(tier 布局/公司节点/未识别 dashed 区/顶层徽标/悬停气泡/拖画布+缩放+适应)、编辑态全套(绘制橙钮/提示条/圆点/拖卡/底点拖出连线/重复「已存在连线」/auto 删边二次确认/保存 DFS 环检测红色高亮/重置确认/完成绘制三选/切视图拦截)、脱敏拍板口径(列表+图谱卡片默认脱敏→点击明文+埋点→显示完整号码批量揭→导出 CSV 恒脱敏)、6.2 快速添加(类别→职务二级联动/空行不提交/查重+确认知悉/首错行定位)、6.3 页内编辑(行内编辑/dirty 高亮/撤销修改/保存全部校验/职务变更触发自动边重建)、右侧规则/状态/日志三 tab 面板(自动边规则/脱敏分歧黄框/vetoed 对/埋点日志)。
- **自检**:Playwright(`.venv10`,file:// 直开)**25/25 PASS,0 console/page error**;截图 7 张在 `prototype/shots/`;结果 JSON:`prototype/selfcheck-result.json`;重跑入口:`prototype/selfcheck.py`。
- **自检期修复 3 处**:① init 缺 `layoutGraph()`(卡片叠原点);② edge-hit path 缺 `fill:none`(贝塞尔闭合区被填成黑块);③ 6.2 入口纠正 = 原型 u193「添加联系人」按钮(OCR快捷新增保持占位,与原型一致)。
- **待验收**:图谱 u536 逐条对照 + 脱敏分歧裁决(规则面板黄框)→ 通过后关票、升格正式 demo(票 06 接真实 API)并重启票 01 grilling。
- **验收反馈修订 ①(2026-09-09 用户)**:未设层级联系人作为连线端点时先弹层输入层级(默认 = 对端层级 ±1)→ 定级后移出「未识别层级」区、自动边按新层级重建;重置/放弃可回退层级(快照含 levels)。已实现 + 自检 29/29 PASS(`shots/05b-level.png`);衍生票 01 待钉点:职务字典空层级被图谱定级覆盖后的持久化语义(回写职务 vs 图谱侧独立存层级)。
- **验收反馈修订 ②(2026-09-09 用户「连不了线」)**:根因 = 三坑叠加——①蓝圆点命中区仅 12×12 且半悬卡外(点偏即变拖卡)→ CSS ::after 扩到 30×30;②顶点拖出无反应(只认底点出发)→ 支持顶点反向拖(dir=up,目标=上级);③初始 12 条 auto 边已覆盖最自然组合,重复拒绝 toast 只有四个字 → 带上具体人名(「已存在连线:王强 → 胡子(蓝色实线即是)」)。自检升级为真实鼠标全链路(`pg.mouse` mousedown→move→up),32/32 PASS。
- **验收反馈修订 ③(2026-09-09 用户)**:层级值域改为 **0–100、100 = 顶层**(用户拍板)→ 字典/数据 level 数值化(100/60/30 为示例);布局改「只有 100 在公司节点上方,0–99 按层级降序分行,未识别 null 进 dashed 区」;图谱卡片右上角加层级徽标(lv-chip);连线定级弹窗改 0–100 数字输入(默认 = 对端层级 ∓10);自动边规则重述为「相邻更高一档,同类别优先」(语义与旧三档版等价)。修复:layoutGraph 改按档强制归行(档位插入使旧位置不可信)。自检 33/33 PASS,`shots/05b-level.png` 确认。实现注:SearchReplace 大 CJK 块再次触发部分落盘(首次调用仅 contacts 一条失败其余落盘),长块改造按 AGENTS.md 降级链改 python 特征行定位切片替换完成。
- **验收反馈修订 ④(2026-09-09 用户)**:①**一个节点最多只有一个上级**(单父约束,手动/自动同样生效:目标已有上级 → 拒绝连线并提示先删旧线);②**层级 100 无上级、下级就是公司** → 自动连「100 → 公司」系统边(可删否决;公司节点不受单父约束,允许多个 100 并列挂上);③自动边改单父化:相邻更高一档(同类别优先)**恰好一人**才自动连,多候选属歧义不自动连留手动(**有层级节点可以没上级**,用户同日补充确认;待票 01 裁决:多候选是否改为择一自动连)。初始 auto 边 12 → 4(3×100→公司 + 邓伦→胡子)。环测试换无上级对(张华↔王大牛)。自检 34/34 PASS(`shots/03-graph.png` 确认),实现仍走 python 特征行定位切片替换(SearchReplace 大 CJK 降级链)。
- **验收反馈修订 ⑤(2026-09-09 用户)**:连线**无向化**——①引脚只管连线,不再区分底点/顶点方向语义,拖任一引脚到对方卡片即可;②**上下级由两端层级自动决定**(层级高者为上级),与拖动引脚无关;③**同级(层级相同)之间不连线**,即时拒绝;④连带推论:连线层级严格递减 → 图恒为有向无环,环在构造上不可能(DFS 检测降为保存时兕底),自检环场景改为「反向同对必命中已存在」;⑤定级弹窗默认值简化为对端层级 −10(定级后方向自动定)。实现:tryConnect 改无向签名 (aId,bId) + 新增 commitDirectionalEdge(同级拒→定方向→单父拒→落 pending);openLevelPicker 确定回调改走 commitDirectionalEdge。自检 34/34 PASS。**⑤补丁(用户当日反馈「同级了连线没清掉」)**:建线后层级再变化(定级/职务变更)会让既有线变同级或方向颠倒 → 新增 sanitizeManualEdges(rebuildAutoEdges 末尾调用):manual committed 边与 pending 新增中,父层级 ≤ 子层级的自动移除并记日志(auto 边由重建构造保证合法)。自检 35/35 PASS。

1243
.scratch/customer-contact-graph/prototype/prototype-contact-graph.html

File diff suppressed because it is too large

176
.scratch/customer-rework/bruno-recon-d.json

@ -1,12 +1,12 @@
{
"report": {
"source_api_count": 265,
"source_key_count": 372,
"generated_file_count": 372,
"source_api_count": 274,
"source_key_count": 381,
"generated_file_count": 381,
"private_file_count": 0,
"unrecognized_count": 0,
"key_conflicts": [],
"keep_count": 372,
"keep_count": 381,
"new_count": 0,
"delete_file_count": 0,
"delete_distinct_url_count": 0
@ -1910,6 +1910,36 @@
"generated": true,
"folder": "项目规则"
},
{
"path": "A7 后台管理/日志管理/批量操作日志/批量下载结果文件.bru",
"key": "POST /api/log/batch/download",
"generated": true,
"folder": "批量操作日志"
},
{
"path": "A7 后台管理/日志管理/批量操作日志/批量操作日志分页.bru",
"key": "POST /api/log/batch/page",
"generated": true,
"folder": "批量操作日志"
},
{
"path": "A7 后台管理/日志管理/批量操作日志/结果文件下载.bru",
"key": "GET /api/log/batch/file",
"generated": true,
"folder": "批量操作日志"
},
{
"path": "A7 后台管理/日志管理/用户操作日志/用户操作日志分页.bru",
"key": "POST /api/log/operation/page",
"generated": true,
"folder": "用户操作日志"
},
{
"path": "A7 后台管理/日志管理/登录日志/登录日志分页.bru",
"key": "POST /api/log/login/page",
"generated": true,
"folder": "登录日志"
},
{
"path": "A7 后台管理/权限管理/权限点管理/上传图标.bru",
"key": "POST /api/resources/icon/upload",
@ -2060,6 +2090,30 @@
"generated": true,
"folder": "用户管理"
},
{
"path": "A7 后台管理/用户管理/同步任务/保存同步设置.bru",
"key": "POST /api/system/sync/settings",
"generated": true,
"folder": "同步任务"
},
{
"path": "A7 后台管理/用户管理/同步任务/同步任务分页.bru",
"key": "POST /api/system/sync/tasks/page",
"generated": true,
"folder": "同步任务"
},
{
"path": "A7 后台管理/用户管理/同步任务/同步任务详情.bru",
"key": "GET /api/system/sync/tasks/detail",
"generated": true,
"folder": "同步任务"
},
{
"path": "A7 后台管理/用户管理/同步任务/读取同步设置.bru",
"key": "GET /api/system/sync/settings",
"generated": true,
"folder": "同步任务"
},
{
"path": "A7 后台管理/用户管理/同步任务/钉钉组织同步.bru",
"key": "POST /api/system/sync/dingtalk",
@ -2468,6 +2522,56 @@
],
"summary": "停用角色(内置角色不可停用)"
},
{
"file": "crm-auth/src/main/java/com/crm/auth/controller/SyncTaskController.java",
"className": "SyncTaskController",
"http": "POST",
"url": "/api/system/sync/dingtalk",
"tags": [
"A7 后台管理/用户管理/同步任务"
],
"summary": "发起钉钉组织同步任务(异步执行,返回任务编号)"
},
{
"file": "crm-auth/src/main/java/com/crm/auth/controller/SyncTaskController.java",
"className": "SyncTaskController",
"http": "POST",
"url": "/api/system/sync/tasks/page",
"tags": [
"A7 后台管理/用户管理/同步任务"
],
"summary": "同步记录分页(任务编号/开始时间范围/触发方式/执行状态筛选,开始时间倒序)"
},
{
"file": "crm-auth/src/main/java/com/crm/auth/controller/SyncTaskController.java",
"className": "SyncTaskController",
"http": "GET",
"url": "/api/system/sync/tasks/detail",
"tags": [
"A7 后台管理/用户管理/同步任务"
],
"summary": "同步任务详情(执行结果/失败明细/进度)"
},
{
"file": "crm-auth/src/main/java/com/crm/auth/controller/SyncTaskController.java",
"className": "SyncTaskController",
"http": "GET",
"url": "/api/system/sync/settings",
"tags": [
"A7 后台管理/用户管理/同步任务"
],
"summary": "读同步设置(含派生的当前计划/下次执行时间/最近修改人)"
},
{
"file": "crm-auth/src/main/java/com/crm/auth/controller/SyncTaskController.java",
"className": "SyncTaskController",
"http": "POST",
"url": "/api/system/sync/settings",
"tags": [
"A7 后台管理/用户管理/同步任务"
],
"summary": "保存同步设置(校验:HH:mm 格式、重试间隔≥1 分钟、重试次数≥1、并发≥1)"
},
{
"file": "crm-auth/src/main/java/com/crm/auth/controller/SystemController.java",
"className": "SystemController",
@ -2588,16 +2692,6 @@
],
"summary": "导出用户列表(按筛选条件,返回文件信息)"
},
{
"file": "crm-auth/src/main/java/com/crm/auth/controller/SystemController.java",
"className": "SystemController",
"http": "POST",
"url": "/api/system/sync/dingtalk",
"tags": [
"A7 后台管理/用户管理/同步任务"
],
"summary": "钉钉组织全量同步(部门树+用户,幂等可重跑)"
},
{
"file": "crm-auth/src/main/java/com/crm/auth/controller/SystemController.java",
"className": "SystemController",
@ -4285,7 +4379,7 @@
"A7 后台管理/系统设置/数据字典/字典项",
"A7 后台管理/业务规则/商机规则/商机相关字典"
],
"summary": "字典项分页(groupId/keyword/status 筛选,附带默认/引用/有效可选标记)"
"summary": "字典项分页(只查一级,二级子项随行内嵌 children 含停用返回;groupId/keyword/status 筛选只作用一级行,附带默认/引用/有效可选标记)"
},
{
"file": "crm-dict/src/main/java/com/crm/dict/controller/DictItemController.java",
@ -4296,7 +4390,7 @@
"A7 后台管理/系统设置/数据字典/字典项",
"A7 后台管理/业务规则/商机规则/商机相关字典"
],
"summary": "新建/编辑字典项(键值必填;编码空自动生成、创建后只读;未被引用且无子项可换组;默认项禁停)"
"summary": "新建/编辑字典项(children 非空即复合保存:子项全量替换——带 id 更新/不带 id 新建且键值=名称/缺失删除且被引用63009内置63003拦;复合新增不传键值、服务端取字典名称;一级或子项可带 isDefault,同载荷最多一行;键值必填63001;编码空自动生成、创建后只读;未被引用且无子项可换组;默认项禁停)"
},
{
"file": "crm-dict/src/main/java/com/crm/dict/controller/DictItemController.java",
@ -5002,6 +5096,56 @@
"A4 客户管理/我的客户/看板列表"
],
"summary": "看板单列卡片"
},
{
"file": "crm-log/src/main/java/com/crm/log/controller/LogController.java",
"className": "LogController",
"http": "POST",
"url": "/api/log/operation/page",
"tags": [
"A7 后台管理/日志管理/用户操作日志"
],
"summary": "用户操作日志分页"
},
{
"file": "crm-log/src/main/java/com/crm/log/controller/LogController.java",
"className": "LogController",
"http": "POST",
"url": "/api/log/login/page",
"tags": [
"A7 后台管理/日志管理/登录日志"
],
"summary": "登录日志分页"
},
{
"file": "crm-log/src/main/java/com/crm/log/controller/LogController.java",
"className": "LogController",
"http": "POST",
"url": "/api/log/batch/page",
"tags": [
"A7 后台管理/日志管理/批量操作日志"
],
"summary": "批量操作日志分页"
},
{
"file": "crm-log/src/main/java/com/crm/log/controller/LogController.java",
"className": "LogController",
"http": "POST",
"url": "/api/log/batch/download",
"tags": [
"A7 后台管理/日志管理/批量操作日志"
],
"summary": "批量下载勾选记录的结果文件(zip 打包)"
},
{
"file": "crm-log/src/main/java/com/crm/log/controller/LogController.java",
"className": "LogController",
"http": "GET",
"url": "/api/log/batch/file",
"tags": [
"A7 后台管理/日志管理/批量操作日志"
],
"summary": "单条批量日志结果文件下载(90 天保留期,过期报文件已过期)"
}
]
}

2
.scratch/customer-rework/bruno_scan_d.py

@ -14,7 +14,7 @@ ROOTS = [
"crm-auth/src/main/java", "crm-file/src/main/java", "crm-lead/src/main/java",
"crm-opportunity/src/main/java", "crm-rule/src/main/java",
"crm-preference/src/main/java", "crm-dict/src/main/java",
"crm-customer/src/main/java",
"crm-customer/src/main/java", "crm-log/src/main/java",
]
PROJECT = r"e:\code\crm-backend-matt"
DOCS = r"E:\code\crm-api-docs"

2250
logs/crm-app.log

File diff suppressed because it is too large

1643
logs/mvn-pkg-a7h05.log

File diff suppressed because it is too large

3732
logs/mvn-test-a7h04.log

File diff suppressed because it is too large
Loading…
Cancel
Save