lzf_0626
6e916aab03
Merge remote-tracking branch 'origin/qyqy_develop' into qyqy_develop
2026-09-12 16:08:39 +08:00
lzf_0626
76923d7e8b
fix(portal): 补齐写操作的必填 body(风控升级/解决、场外六个写接口)
...
- 风控:escalations 要 reason、resolutions 要 resolution(字段名不同),
且真实顺序是 先确认接收 才能升级/解决(否则 409 请先确认接收预警)
- 场外:六个写接口都必填 operator_id(防伪校验,须等于当前登录用户),
confirmations 还要 decision(中文枚举)、notifications 还要 notification_type、
recalculate 要 fund_code+application_date;门户自动带当前 user_id
- 实测:recalculate 200 code=0;不传 operator_id 必得 422(证明该字段必须)
- 清单 §3/§4 更新为实测结果并列出各写接口的必填字段表
2026-09-12 16:08:38 +08:00
ZSY_merge
e25296f30a
merge: integrate origin/qyqy_develop ( 552055ee)
2026-09-12 16:05:24 +08:00
lzf_0626
552055ee2d
fix(portal): 修风控预警列表 422(limit 上限是 5,改为不传);运营邮件改用 page/page_size;审计 limit 钳制到 1-100
...
- risk/alerts 传 limit=20 会 422 query.limit<=5,导致整张表格渲染不出来、
行内确认/升级/解决按钮随之消失 —— 这正是验收清单 3-3/3-6~3-9 看不到功能的原因
- 实测列表返回 2 条预警;处置接口有状态前置条件(409 = 当前状态不允许)
- 清单 3-3/3-6 更新为实测结果并写明 limit 上限与状态机约束
2026-09-12 16:03:22 +08:00
ZSY_merge_bot
4c651ccce1
merge: integrate latest origin/qyqy_develop (frontend acceptance checklist + portal flow wizard)
2026-09-12 15:58:08 +08:00
张胜宇
ebc3fe4cbe
feat(§T): 账户看板 + 场内模拟交易 9 端点(用户自助首版)
...
新增 §T 用户自助段(docs/05 §19 新号段 7 个 = A×40/C×7/K×4/M×4/O×3/R×4/T×9):
- T001 GET /api/v1/users/me/account/dashboard — 账户/资金/持仓/盈亏汇总
- T002 POST /api/v1/users/me/orders — 委托提交(首版 market 立即全额成交)
- T003 / T004 / T005 委托列表/详情/撤单
- T006 GET /api/v1/users/me/holdings — 持仓列表(含市值/盈亏/当日盈亏)
- T007 / T008 成交记录列表/详情
- T009 GET /api/v1/users/me/cash-ledger — 资金账本
要点(与 docs/00 §6.6 一致):
- 首版市价委托立即全额成交,不实现撮合队列/部分成交;T005 撤单首版对任何在场委托返回 ORDER_NOT_CANCELLABLE (409)
- 价格来源复用 base FundQuoteService;service 层不二次封装(满足 AGENTS 第 2 条)
- 首版风控 3 条硬性:产品可交易、客户适当性、持仓比例上限(fin_market_price 缺失或过期 → 拒绝买入)
- 数据库零修改:10 张 fin_* 表全部 docs/00 既定,本批 PR 改列类型与可空性均 0;底座实际偏差(id 无 AUTO_INCREMENT、所谓'生成列'是普通 NOT NULL)由 service _next_id / 业务派生值补偿
注册 API:9 端点均注册进 app.main;user=9001(cust)'s id 写账
权限码(tools/seed_test_rbac.py 同步登记 + CUSTOMER 全量):
9047 account:read:self
9048 trade:order:create
9049 trade:order:read
9050 trade:order:cancel
9051 holding:read:self
9052 trade:txn:read
错误码(app/core/errors.py + docs/05 §3.6 + tests/unit/core/test_errors.py DOCUMENTED 三方同步):
404 ACCOUNT_NOT_FOUND / ORDER_NOT_FOUND
409 ORDER_NOT_CANCELLABLE
422 INSUFFICIENT_FUNDS / INSUFFICIENT_HOLDING / HOLDING_RATIO_EXCEEDED / SUITABILITY_MISMATCH / PRODUCT_NOT_TRADABLE
503 FUND_QUOTE_UNAVAILABLE(可重试)
新增:app/api/controllers/trading.py / app/api/schemas/trading.py / app/service/trade_service.py / tools/seed_sim_account_demo.py / tests/unit/service/test_trade_service.py(unit×8) / tests/contract/test_trading_endpoint_contract.py(contract×11)
修改:app/main.py(挂载 controller) / app/core/errors.py(10 新异常类) / tools/seed_test_rbac.py / docs/05-接口文档.md(§19 T001-T009 + §3.6 9 新码) / tests/unit/core/test_errors.py(DOCUMENTED 同步)
门禁:pytest tests/unit tests/contract 1313 passed (+19 新增) / ruff all clean / 三道守卫全过
2026-09-12 15:50:37 +08:00
lzf_0626
49ebda4cb7
docs: 新增前端验收清单(64 项,逐项给出预期结果,区分实测与按契约)
2026-09-12 15:48:31 +08:00
lzf_0626
2525fbdad9
feat(portal): 推广材料补成完整流程向导,并改为按 body.code 判成败
...
- 投顾视图:创建 -> 补结构化输入(含七项费率) -> 生成 -> 投递 -> 查询
生成后自动把 material_version_id 填进投递框
- 管理员视图:新增推广材料审核(promotion:review 只给管理员,职责分离)
- 统一结果渲染改看 body.code:本平台业务失败也返回 HTTP 200
(生成失败是 HTTP 200 + code=422 + '材料内容未通过合规校验')
- 输入骨架刻意不预填业绩(performance_info 有 show_* 开关可关),
费率填占位文本以便跑通流程;真实材料必须换成真实值
2026-09-12 15:45:09 +08:00
lzf_0626
615032ab00
fix: 修 POST /api/v1/conversations 的 MissingGreenlet(会话建不出来导致转人工 404)
...
- public_platform_service: 创建 ConversationSession 时显式赋值四个 server_default 时间列,
否则 flush() 后需回读数据库生成值,在 async session 里以同步属性访问触发
MissingGreenlet,接口 500,连带转人工一直报会话不存在
- portal: 客服改用 C001 真实建会话;转人工改传 reason_code/reason_detail(原 reason 属额外字段 422)
- portal: 投顾查客户投资目标走 customers/{id} 变体
- seed/grant: 补 investment-goal:{read,write,confirm}:customer 三个动态拼出的权限码
(data_scope=own_customers,投顾只看名下客户)
2026-09-12 15:37:36 +08:00
lzf_0626
14f5078491
Merge remote-tracking branch 'origin/qyqy_develop' into qyqy_develop
2026-09-12 15:21:36 +08:00
lzf_0626
bfd622b592
feat: 统一登录门户(按角色分流五套工作台)+ 补齐权限覆盖缺口
...
- tools/portal.py:客户/风控/运营/管理员/投顾五个工作台,走真实登录与真实接口
- 新增 6 个此前从未建过的权限码(promotion:* 4 个 / financial:nl2sql:read / probe:read),
它们让推广材料与金融 NL2SQL 两条线对所有角色都是 403
- 投顾权限 10 → 25 项:补投资目标创建确认、agent:run、行情、知识检索、客户画像、推广材料
- 运营补 financial:nl2sql:read;create_test_user.py 不再硬编码角色 id(改按 role_code 查库)
- portal 的写请求补 Idempotency-Key 头(漏了会被 AGENT_INPUT_INVALID 拒)
- 新增 tools/check_permission_coverage.py 做权限对账
2026-09-12 15:21:34 +08:00
zhangshy
164d55a05a
修复合规问句被误判为收益承诺
2026-09-12 15:05:03 +08:00
lzf_0626
4c00db5636
docs: docs/32 工具索引补上功能测试台与两个新守卫,修正已过期的权限号段口径
2026-09-12 14:57:56 +08:00
lzf_0626
c8d5fe11dc
feat: 新增平台功能测试台(自动覆盖 docs/05 §19 全部端点 + 一键只读冒烟 + 按身份测权限)
2026-09-12 14:57:17 +08:00
zhangshy
5634fdc023
完善风控登录权限和模型能力配置
2026-09-12 14:43:05 +08:00
Windows
026fec7514
Merge remote-tracking branch 'origin/qyqy_develop' into lzl_qyqy_integration
2026-09-12 14:41:13 +08:00
Windows
9c6f839a6e
feat: add advisor demo environment bootstrap
2026-09-12 14:40:53 +08:00
lzf_0626
c200a61cd5
docs: docs/32 更新到 2026-09-12 状态,补功能性测试的触发条件与外部依赖前置
2026-09-12 14:22:34 +08:00
lzf_0626
c8cdc06e80
Merge remote-tracking branch 'origin/qyqy_develop' into qyqy_develop
2026-09-12 14:18:06 +08:00
lzf_0626
ffbcc229c2
fix: 删掉 NL 合并后残留的未使用变量 role_ids(ruff F841)
2026-09-12 14:17:42 +08:00
Windows
050eedac68
Merge remote-tracking branch 'origin/qyqy_develop' into lzl_qyqy_integration
2026-09-12 14:16:27 +08:00
Windows
54fadb3a99
Merge branch 'qyqy_develop' of http://47.106.207.27:3000/AI260626/group_fqcd_jr into lzl_qyqy_integration
...
# Please enter a commit message to explain why this merge is necessary,
# especially if it merges an updated upstream into a topic branch.
#
# Lines starting with '#' will be ignored, and an empty message aborts
# the commit.
2026-09-12 14:15:43 +08:00
wangjianlong_0626
58c28ef4a0
fix(memory): 记忆抽取容忍"单元素数组"形状,空结果不再被判为无效 JSON
...
## 现象(2026-09-12 跑真实 Worker 时发现)
`episode_worker` 反复报 `模型记忆抽取输出不是有效 JSON` 并重试到失败:
```
pydantic_core.ValidationError: Input should be a valid dictionary or instance of _ExtractionPayload
input_value=[{'memory_key': None, 'value': None, 'memory_type': None, 'confidence': 0}]
input_type=list
```
## 根因
契约是**对象** `{...}`,而模型在 episode 抽取路径会返回**单元素数组** `[{...}]`。
`_parse` 直接 `json.loads` 后交给 pydantic,数组自然过不了 `model_validate`,
于是被归入"输出不是有效 JSON"这一条 —— 但它其实是**合法的空结果**
(`_validate` 已能把"三字段为 null 且 confidence=0"正确识别为"无持久事实",返回 None)。
后果:这类 episode 白跑一遍模型调用、重试到 `retry_count` 上限后判失败,
**该片段的记忆永远抽不出来**。
## 修法
`_parse` 里只对"**恰好一个对象**的数组"做归一化:
- `[{...}]` → 取 `{...}`(空结果照常返回 None;有事实照常解析)
- 多元素数组、元素非对象、空数组 → **不猜**,仍交给校验失败关闭
(多元素时无法判断哪个是答案,猜错会把错误记忆写进库,比失败更糟)
## 测试
`tests/unit/service/test_memory_extraction_service.py` 追加 2 个用例:
- `test_single_element_array_is_normalized`:数组包空结果 → None;数组包有事实 → 正常解析
- `test_multi_element_or_non_dict_array_still_fails_closed`:多元素 / 非对象元素 / 空数组 → 仍失败
## 验证
- `pytest tests/unit/service/test_memory_extraction_service.py tests/unit/worker/test_memory_extraction_worker.py` → 28 passed
- `mypy app` → 0 错 / 245 文件
> 说明:`memory_extraction_service.py` 属主干线代码。此处是从**实际运行日志**里发现的
> 健壮性缺陷,改动限定在输出形状归一化,不改变抽取契约与校验规则。
2026-09-12 14:11:42 +08:00
wangjianlong_0626
be970aa46a
chore: 同步测试基线(1415 passed);合并主干袁聪场外线 4 提交
2026-09-12 14:08:50 +08:00
wangjianlong_0626
f7b35006ff
Merge remote-tracking branch 'origin/qyqy_develop' into nl-merge-colleague
2026-09-12 14:06:18 +08:00
wangjianlong_0626
67ba1b8eee
docs: 标明 neo4j_profile_projection 当前未被生产装配(方案 A 取舍)及启用前提
...
该适配器与主干 `ProfileGraphProjectionService` 是同一件事的两套实现,对图的建模不同:
本模块按客户各建**私有** `Preference`/`goal` 节点、数据源是 `memory_unit`;
主干服务写**共享** tag 节点、数据源是 `user_facts` 且只投影已确认事实。
## 它不是"本来就没被装配"
| 提交 | 事件 |
|---|---|
| `f167390`(ZSY) | 新建该适配器 |
| `5e848f5`(ZSY) | `feat: wire neo4j projection into worker` —— 在 `__main__.py` 装配,此后一直是**活的** |
| `4d8edb4`(主干) | PR #7 合并后接线仍在,**仍是活的** |
| `57677f6`(本次合并) | 主动摘掉那段装配 ⇒ 失去生产引用 |
`__main__.py` 在本次合并中并没有冲突(git 自动取的是带接线的主干版本),
是本线解决完冲突后**主动手工删除**的。
## 但根本原因是它与方案 A 互斥
只要落实方案 A,它就必然失去引用 —— "删 `__main__.py` 接线、保留 runtime 那套"与
"保留 `__main__.py` 骨架、把它的 neo4j handler 换成主干服务"两种做法结果相同。
所以这不是方案 A 的副作用,而是"两套图投影本来就只能活一套"。
## 改动
- `app/infrastructure/neo4j_profile_projection.py` 文件头加 `.. warning::`:
写明当前未被生产装配、为什么、其单测保护的是**模块自身契约**而非"已装配",
以及**启用前提** —— 必须先决定"图的节点模型以谁为准",只加回 `__main__.py` 装配
会重新变成两套图投影并存。
- `docs/39-主干合并对策记录.md` §3.3 补完整时间线与上述论证;§6 第 1 条改为准确表述。
- **保留文件**(实现本身完整:`MERGE` 幂等、按 `profile_version` 判重不被旧版本覆盖、
写入前经 `sanitize_customer_service_message` 脱敏),去留待架构师定:
删除 / 保留为参考实现(当前取此)/ 反过来改用它(则方案 A 需重议)。
验证:`mypy app` → 245 文件 0 错;该模块 4 个单测通过;文档守卫 53 份无编号冲突。
2026-09-12 13:23:43 +08:00
wangjianlong_0626
91f1efcada
chore: 同步测试基线到合并主干后的实测值(1414 passed / mypy 245 文件)
2026-09-12 13:18:16 +08:00
yuancong_0626
fedbf5a3ef
merge: sync latest origin/qyqy_develop before push
2026-09-12 13:16:57 +08:00
wangjianlong_0626
cbca2cccbb
Merge remote-tracking branch 'origin/qyqy_develop' into nl-merge-colleague
2026-09-12 13:15:33 +08:00
wangjianlong_0626
57677f6554
merge: 合并主干 qyqy_develop(PR #7 之后)并对齐两套投影实现
...
共同祖先 bbf623a;主干 54 个提交、118 个文件;本线 25 个文件;9 个冲突文件。
主干这次把 **ZSY 的整条投影实现合进来了(PR #7)**,而本线此前的提交正是
移植并修正同一套代码 —— 因此冲突的本质是"同一功能两份实现并存",取舍错了会把
已修好的缺陷又带回来。逐项取舍与理由见 `docs/39-主干合并对策记录.md`。
## 取舍(9 个冲突)
取本线:
- `app/infrastructure/milvus_profile_projection.py` —— 主干是 ZSY 原版,含两处必炸点:
① `customer_id` 要求 int 而本仓所有生产者都写 `str` ⇒ 每个事件必然失败;
② 不可投影的 `memory_key` 直接 raise ⇒ 一条 `constraint:` 记忆毒死整客户整批。
本线版已放宽为「接受纯数字字符串」与「跳过并留痕」。
- `memory_sync_outbox_worker.py` / `conversation_privacy.py` / `risk_questionnaire.py`
—— 代码逐行一致,仅注释与说明文字详略不同(`risk_questionnaire.py` 两边**独立做了
完全相同的修复**,都改成 re-export `app.model.profile`)。
- 两个投影测试文件 —— 本线是他那份的**超集**(4→10、4→5 例,包含他全部用例)。
两边合并:
- `app/worker/runtime.py`:`__init__` 两边各加一个参数,都要。
- `app/service/agent/implementations/customer_service.py`:import 取并集;
`COMPANY` 取主干的「奶龙基金责任有限公司」("奶龙"是本项目实际品牌名,主干多处出现),
`HOTLINE`/`SERVICE_HOURS` **取本线的修复**(主干仍是占位符 `400-XXX-XXXX`,
本线已改为引用 `customer_service_rules` 的唯一来源 —— 这是 A1 缺陷修复,
否则同一客服给客户两个不同号码)。
- `AGENTS.md`:表数/Agent 清单取主干(90/89、7 个 Agent),本线的
`-X utf8` 与两条 outbox 易错点保留,测试基线按合并后实测重算。
## 消费端只保留一套(本次最重要的一处)
合并后曾出现**两套消费者读同一个 `memory_sync_outbox`**:`__main__.py`(PR #7)
与 `runtime.consume_profile_projections()`(本线),而**两者的 neo4j handler 不同**
—— 前者用 ZSY 的 `Neo4jProfileProjection`(按客户各建私有节点),
后者用主干 `ProfileGraphProjectionService`(共享 tag 节点、只投影已确认事实)。
同一事件被谁领到结果不定,等于"同一事实在图里有两种说法",正是**方案 A 要避免的状态**。
现只保留 runtime 那一套(带 `memory_sources` 兜底、neo4j 复用主干服务),
删除 `__main__.py` 的重复接线;装配入口职责仍在该文件(注入 `relationships` /
`projection_cleaner`),Milvus 客户端由 `bootstrap` 工厂惰性构造、缺配置时显式降级。
副作用:`app/infrastructure/neo4j_profile_projection.py` 不再被生产代码引用,成为
**死代码**(本线未删,属架构师线,其单测仍在)—— 待架构师决定删或明确分工。
## 顺带修掉的 3 个继承缺陷(主干同样存在,PR #7 后未整套复跑故未发现)
1. `tools/seed_test_rbac.py` **少建 `review_t`(9004)账号** —— 两个集成测试都依赖它
("账号存在但无权限应返回 200 空集而非 404"、`PLACEHOLDER_ACCOUNTS`)。
同时把用户↔角色绑定从 `zip(..., strict=True)` 改为**显式配对表**:原写法隐含
"USERS 与 ROLES 一一对应",一加不绑角色的账号就 ValueError、整个种子跑不完
(commit 在最后,外部表现是"什么都没发生")。
2. `CustomerProfileCandidateService._write_profile_snapshot` **漏写 `current_customer_id`**
—— 该列不是生成列而是普通可空列 + 唯一键 `uk_profile_snapshot_current`,
不写则唯一键形同虚设(多个 NULL 不冲突),且旧当前版本也没清该列、补写就会撞键。
现旧值置 None、新值显式写入(与 `ProfileGenerationService._clear_current` 一致)。
3. 集成测试前置未记录 —— 13 个登录/RBAC 用例因 401 而红,实为"测试账号不存在",
跑 `seed_test_rbac.py` + `set_user_password.py` 后转绿;已在 `AGENTS.md` 记明,
避免被误判成代码缺陷。
## 文档
- 新增 `docs/39-主干合并对策记录.md`(逐文件取舍 + 理由 + 遗留)
- `docs/37` 订正一处过时说法:曾写 `current_customer_id` 无人使用且故意不映射,
实际 `app/model/profile.py` 已映射且有人使用(详见该文档 §6.2 的订正块)
- 文档编号:主干已占 29–36,本线两份文档让号至 `docs/37`、`docs/38`
## 验证(合并后实测)
- `pytest tests`(全量)→ `2 failed, 1396 passed, 2 skipped`
- `pytest tests/integration` → `102 passed, 1 skipped`(修上述 1、2 后从 15 failed 归零)
- `mypy app` → `Success: no issues found in 245 source files`
- `tools/audit_schema.py` → 89 张业务表无缺失/意外(未改动任何表结构)
- `tools/check_authoritative_docs.py` → 52 份文档无编号冲突
- `tools/check_rbac_seed_consistency.py` → 通过
那 2 个失败是既有环境项(`test_offsite_document_recognition_adapter.py` 断言请求体
中文原文而 httpx 序列化成 `\uXXXX`),与本次合并无关。
2026-09-12 13:14:57 +08:00
yuancong_0626
a0a489a055
chore: preserve RBAC seed consistency checks
2026-09-12 13:03:16 +08:00
wangjianlong_0626
3e081addf0
docs: docs/32、docs/33 让号至 37、38(主干续占 32–36);同步引用
...
主干 `qyqy_develop` 已占用 29–36(29 登录接口 / 30 投顾迁移TODO / 31 投顾灰度 /
32 平台侧交接与联调准备 / 33–35 ZSY 底座扩展确认往来 / 36 PR7 合并记录),
本线原用的 32、33 与之重号。按"以架构师为主线"继续让号。
- `docs/32-记忆投影链路实现说明.md` → `docs/37-记忆投影链路实现说明.md`
- `docs/33-架构对齐-记忆与画像投影链路.md` → `docs/38-架构对齐-记忆与画像投影链路.md`
(两次 `git mv`,保留文件历史)
- 同步更新 `AGENTS.md` 内 2 处引用、`docs/38` 内 2 处交叉引用
- 文档内部无自引用编号;全仓已无其它 `docs/32`、`docs/33` 引用(全量搜索确认)
验证:`tools/check_authoritative_docs.py` → checked 41 documents, no number collision
2026-09-12 13:00:30 +08:00
张胜宇
c5adf01603
test: 补端点编号守卫与权限判定的 HTTP 层用例
...
补两道守卫,覆盖 2026-09-12 那次合并评审暴露出来的两个盲区。
一、`docs/05` §19 端点编号唯一性(新增 `tools/check_docs_endpoint_ids.py`)
- 背景:`tools/check_authoritative_docs.py` 只校验 `docs/` 的**文件名编号**,不校验
§19 的**端点编号**。两条线各自新增端点时都占了 `A034`/`A035`,合并后 §19 同时
存在两个 `A034` 与两个 `A035`,而文档守卫照样通过 —— 编号复用会静默遗留。
- 口径要点:**不枚举前缀白名单**,前缀从数据里归纳后连同数量一起输出。同一件事上
还踩过一次"扫描正则写成 `[AMKCS]`,漏掉 `O`/`R` 两段,把 62 个端点报成 55 个"——
漏掉的号段一旦被复用,脚本仍会报"重复 0"。所以覆盖报告会打印
`62 个端点编号 / 6 个号段(A×40、C×7、K×4、M×4、O×3、R×4)`,让漏扫本身可见。
- 只读、不依赖任何环境变量(纯解析文档),可在裸检出环境直接跑。
二、端点权限判定的 HTTP 层用例(新增 `tests/unit/api/test_permission_enforcement.py`)
- 背景:既有用例都通过 `build_request_context` 覆写注入**已经带好权限**的上下文,
只覆盖"有权限能通",覆盖不到"缺权限必须被拒"。而"权限码在库里根本不存在"这类
环境数据问题(本次三个新权限码)恰恰只会在这一层暴露:权限判定发生在身份解析之后,
服务层测试自己构造 `RequestContext`,权限字段由测试塞入,所以全绿也照样漏。
- 覆盖客服二期三个权限码对应的 5 个端点:
`handover:read`(工单列表/详情)、`memory:candidate:review`(候选列表/审核)、
`memory:candidate:confirm`(用户确认)。
- 正反双向断言:缺权限 → 必须 403 且错误码为 `AGENT_PERMISSION_DENIED`;
带权限 → **不能**再是 403(这一条把端点要求的权限码钉住,改动即红)。
- 只替换两处边界:`build_request_context`(跳过 JWT 与身份库)与
`AuthorizationService` 的审计落库(内存替身);真实路由、真实权限判定与真实 403 信封。
- 已做反向验证:给一个不存在的权限码时,5 个端点全部被拒(403),确认正向断言非空过。
验证(隔离 worktree,基线 `origin/qyqy_develop` = 4d8edb4):
pytest tests/unit tests/contract -> 1294 passed, 2 skipped, 0 failed;
ruff check app tests tools alembic 全通过;mypy app 244 源文件 0 错;
check_authoritative_docs(50 份文档无撞号)与本脚本均通过。
2026-09-12 12:57:16 +08:00
yuancong_0626
a501060e50
merge: merge yc into qyqy_develop
2026-09-12 12:50:14 +08:00
lzf_0626
4d8edb4b7f
chore: 号段一致性自检固化为 tools 脚本并纳入门禁;AGENTS.md 更新主分支口径、Agent/工具清单与测试基线
2026-09-12 12:29:20 +08:00
lzf_0626
4cc0ecdea9
fix: 种子改用 UPSERT 建用户且不覆盖密码,解除投顾 FK 导致的跑不通;记录 RBAC 对齐最终状态
2026-09-12 12:22:32 +08:00
yuancong_0626
91f5373cfb
袁聪的第三次提交,API接口完善
2026-09-12 12:20:01 +08:00
lzf_0626
39f7b81200
docs: 记录本机 RBAC 对齐的实际执行结果,以及 seed_test_rbac.py 被投顾 FK 挡住这一既有破坏
2026-09-12 12:16:41 +08:00
Windows
3751891574
Merge branch 'qyqy_develop' of http://47.106.207.27:3000/AI260626/group_fqcd_jr into lzl_qyqy_integration
2026-09-12 12:10:41 +08:00
Windows
f094cbeae1
fix: harden advisor market history dual-source sync
2026-09-12 12:10:10 +08:00
lzf_0626
e0468a9d9f
docs: 记录 PR #7 合并门禁结果、两处顺手修正与权限号段冲突的来龙去脉
2026-09-12 12:10:03 +08:00
lzf_0626
cdb2de3e45
chore: 权限号段根治——9041-9046 并进种子;修正 grant 脚本与种子 9020-9034 的 id 映射冲突
2026-09-12 12:09:55 +08:00
lzf_0626
2bb056e516
fix: 修正合并进来的 test_security.py 两处密钥路径漂移(漏 dev/,标准布局下必红 2 个用例)
2026-09-12 12:09:55 +08:00
lzf_0626
441364437c
merge: 合并 ZSY 的客服 Agent 接入(访客身份、画像候选、转人工工单)—— PR #7
2026-09-12 12:05:25 +08:00
lzf_0626
c7226d0b69
回复 ZSY 的处置回执 v3:核实两处修正;更正端点编号计数(62 非 55);预检合并树并定位 test_security.py 两处密钥路径漂移
2026-09-12 12:04:18 +08:00
张胜宇
f68b052a69
fix: 端点编号去重与 milvus-lite 降为可选依赖
...
按 qyqy 在 PR #7 评审中的要求处理两项:
- docs/05-接口文档.md §19:客服画像候选的两个端点由 A034/A035 改为 A039/A040。
原编号与 qyqy 侧登录 / RBAC 只读接口(A034-A038)重复;docs 守卫脚本只校验
文档文件名编号、不校验 §19 端点编号,因此该重复会静默遗留。改后全表 55 个
端点编号唯一。
- pyproject.toml / requirements.txt:milvus-lite 由主 dependencies 挪到
[project.optional-dependencies] dev;requirements.txt 只保留说明性注释,
不再作为生效依赖。理由:它仅用于本地开发(Docker Milvus 未运行时的本地
持久化向量库),进主依赖会让生产环境多背一个包。
验证:pytest tests/unit tests/contract -> 1275 passed, 2 skipped, 0 failed;
ruff check app tests tools alembic 通过;mypy app 通过(244 个源文件)。
2026-09-12 11:53:35 +08:00
lzf_0626
ade5e0c085
回复 ZSY 的底座扩展确认 v2:逐行核验两句确认;指出 docs/05 端点编号撞车、三个新权限未随代码合并;补权限种子脚本
2026-09-12 11:46:47 +08:00
lzf_0626
244f03917b
回复 ZSY 的底座扩展确认;登记 docs/00 的一处已知偏差;更新 AGENTS.md 过期口径
...
## 对 ZSY 三件事的核实与裁决(docs/33-ZSY底座扩展确认-回复.md)
1. **访客身份**:`data_scope="public"` 我扫了全平台 26 个消费点,对未知值的行为一致
fail closed(6 处 `== "all"` 判假、1 处 `!= "all"` 会要求 customer_ids 非空否则 403、
1 处显式白名单直接拒),**安全,不用改**。但有一条硬约束必须先确认:全平台有
**72 处 `int(context.user_id)`,只有 3 处做了防御**——若访客的 sub 不是纯数字,
其中"权限被拒时要写审计"的路径会把本该 403 的情况变成 500。已要求他确认
sub 为纯十进制数字、且 visitor 分支复用同一套 `jwt.decode`(含 isdecimal 校验),
而不是另写第二个鉴权入口。
2. **客服 Agent**:`allowed_roles` 扩集合与确定性安全路由可接受;`recalls_customer_memory`
是新声明字段,已要求**默认值必须是 True**(否则会静默关掉所有既有 Agent 的记忆召回,
界面看不出来、只表现为回答变差)。品牌名(南方科技 → 奶龙基金责任有限公司)属业务
口径,已上报项目方定,不由技术侧拍板。
3. **current_customer_id**:**他的判断正确**。我独立实测 information_schema:
`EXTRA=''` 且 `GENERATION_EXPRESSION=''`,配合 baseline_generated.sql 无 GENERATED
子句、seed_profile_demo.py 的注释、profile_repository.py 用原生 SQL 显式写入,
四处一致 ⇒ `docs/00` 第 783 行"生成列"的描述是错的。处置:按真实 schema 映射成
普通可空列、**不改 docs/00**(规则 1)、**不补迁移**(DDL 本身正确,为让文档成真而加
生成列会改变既有列语义,违反规则 4)、但**必须登记这处偏差**。
另:他删了两个死代码文件(含一处硬编码 Milvus 字段名,违反 AGENTS.md §E),方向认同,
但要求他在 PR 里附上两条 git grep 的实际输出以证明"全仓唯一引用是它自己的单测"。
## 落实我在回复里承诺的两件事
- `docs/08-数据库结构审计基线.md` 新增"六、已知文档偏差",逐条登记上述偏差(含四处
证据与处置口径),并注明发现方式;
- `AGENTS.md` 环境口径新增 `MILVUS_LOCAL_URI` 的坑:配了会让健康检查与部分检索指向
本地 Milvus Lite 文件,出现"健康检查正常、实际查的是另一个库";并注明 `milvus-lite`
属本地开发依赖,应放 `optional-dependencies` 而非主依赖。
## 顺带更新 AGENTS.md 的过期口径
- 表数 68 → **89**(场内 51 + 场外/推广 17 + 投顾 21),并注明投顾那 21 张的登记文档待补;
- 测试基线从"1034 passed / 1 failed"改为实测值:ruff 干净 / mypy 228 文件 0 错 /
1207 passed 0 failed / integration 99 passed,并指向 docs/32;
- mypy 那条从"本机 181 错、双方不可比"改成可操作的判据:先对版本,根因是某一侧虚拟环境
没满足 pyproject 的 sqlalchemy>=2.0,<3 / mypy>=1.14,<2;并写明**不要装 sqlalchemy2-stubs**
(那是给 1.4 的,2.0 自带 py.typed,装了反而按 1.4 API 报一批新错)。
文档守卫:42 份无编号冲突。
2026-09-12 11:27:28 +08:00
wangjianlong_0626
57f56bbcbe
fix(profile): 修 profile_snapshots 重复定义(会打挂 Worker);消费端兜底 memory_sources
...
## 1. 独立缺陷:`profile_snapshots` 被两个 ORM 类重复映射
排查 `memory_sync_outbox` 中 `target_store='neo4j'` 那行 `last_error='InvalidRequestError'`
时发现,根因不在图库,而在模型层:
- `app/model/profile.py` → `ProfileSnapshot` 映射 `profile_snapshots`
- `app/model/risk_questionnaire.py` → **另一个** `ProfileSnapshot` 也映射 `profile_snapshots`
SQLAlchemy 不允许两个类映射同一张表。实测:
| 场景 | 结果 |
|---|---|
| 单独导入 `app.main` / `app.worker.runtime` | 正常 |
| 单独导入 `profile_assembly_service` / `risk_questionnaire_service` | 正常 |
| **两者同时导入** | `InvalidRequestError: Table 'profile_snapshots' is already defined` |
Worker 在同一进程里既要处理 `profile.rebuild_requested`(走 `app.model.profile`),
又要处理投顾风险问卷(走 `risk_questionnaire.py`)——所以这是**会打挂 Worker 的缺陷**,
不是理论风险。
修法:`app/model/risk_questionnaire.py` 不再重复定义,改为从 `app.model.profile`
转出(re-export),既有 4 处 `from app.model.risk_questionnaire import ProfileSnapshot`
无需改动。原定义多映射的 `current_customer_id` 经全仓核查无人使用,故不保留
(`app.model.profile` 明确注明该列由数据库维护、故意不映射)。
## 2. 消费端兜底 `memory_sources`
`memory_sources` 是本线新增的投影入参,而投顾线两处生产者的 payload
(`{customer_id, profile_uuid, version, profile}`)没有这个键,原样会导致它们每次画像
变更都 `memory_sources is invalid` → 重试至死信。
新增 `WorkerRuntime._with_memory_sources()`:**键缺失或为 None** 时回退查询该客户
`memory_unit` 中 `status='active'` 的记忆,并记 warning(使"谁没提供"保持可见)。
语义成立:长期记忆是**客户级**而非画像版本级的,每条记忆自带 `version`,
适配器按 `memory_uuid + version` 幂等,故"用的是哪一版"仍确定。
**兜底不掩盖真错误**:键**存在但格式不对**时**不兜底**,原样交给适配器失败关闭。
实现上用键存在性判断而非 `isinstance`——后者会把"缺失"与"格式错"混为一谈,
那是初版实现里的一个真 bug,被新测试抓出后修正。
## 3. 补上此前欠缺的消费端全路径验证
此前"整合验证"是直接调适配器,跳过了 outbox 的领取→分派→状态更新。
- **失败分支**(Milvus 断开时实测):行被领取、按 target_store 分派、异常被捕获、
`status`/`retry_count`/`last_error`/`next_retry_at` 正确落库。
- **成功分支**(注入替身向量客户端):outbox 行 → `processed`、`processed_at` 已写;
不可投影的 `constraint:` 被跳过(只写 1 行);维度 1024;字符串客户号转 int;
**手机号脱敏生效**(`稳健型投资者,手机号 [手机号已隐藏] 请勿外泄`)。
- **兜底实证**:历史行 `id=5`(payload 无 `memory_sources`)经兜底后成功投递为 `processed`。
- **修复实证**:`id=6` 的 `last_error` 从 `InvalidRequestError` 变为
`RecoverableAgentError`(图库不可用)——证明重复定义缺陷确已消除,剩下的是环境问题。
## 4. 测试与验证
- 新增 `tests/unit/worker/test_runtime_profile_projection.py`(5 用例:已提供原样透传、
缺失兜底、空记忆给空列表而非删键、无客户号不兜底、格式错不兜底)
- 全量:`2 failed, 1312 passed, 2 skipped`(2 个失败为既有环境项,非本次引入)
- mypy:`Success: no issues found in 227 source files`
- 表结构审计:89 张业务表无缺失/意外(未改动任何表结构)
- 文档守卫:41 份文档无编号冲突
## 5. 文档
`docs/32-记忆投影链路实现说明.md` 增补 §6.1(兜底)、§6.2(重复定义缺陷)、
§7.1(消费端全路径验证)并更新验证表与文件清单;
`AGENTS.md` 新增"一张表只能有一个 ORM 类"易错点、校正测试基线数字。
## 未做
未改 `docs/00` 基线、未动数据库迁移、未改投顾线生产者代码、未启动常驻 Worker。
遗留:投顾线两处生产者的 payload 仍缺 `memory_sources`(已有兜底,不再死信,
但根治应由投顾线确认);Milvus/Neo4j 容器本轮不可用(Docker Desktop 崩溃),
`id=6` 停在 failed 属环境不可用、非代码缺陷。
2026-09-12 11:24:50 +08:00
lzf_0626
39f261da14
新增 docs/32-平台侧交接与联调准备.md
...
把当前状态整理成可交接的一份:分支与门禁、三条线合进来的内容、平台侧补齐的登录与
RBAC 只读接口、修掉的真缺陷、演示账号与环境口径、新增工具索引、联调清单、悬置决策项。
其中三条是这一轮实际踩出来的,值得留档:
1. 环境数据不随代码合并(config_release / sys_role / sys_permission / Milvus schema /
advisor_product_*)—— 三条组员线各自都在这上面栽过一次;
2. 合并后必须**单独**跑 alembic upgrade head 与 tools/check_authoritative_docs.py:
三家合并里两次带进文档重号、两次库与代码版本不一致(一次"版本号跑了表没建"、
一次"表没建版本号也没跑"),两者都靠 upgrade 收敛,重号只有守卫能发现;
3. 投顾演示数据里"方案"那步配不出来,缺的是**证据来源**(source_url / document_sha256),
必须来自真实的销售适当性披露或基金合同文件;不伪造这两个字段是关键红线。
另记下环境口径:mypy 与测试数必须带环境读,NL 那边 184 错与本机 0 错的差异根因是
对方的 .venv 没满足 pyproject 的 sqlalchemy>=2.0,<3 / mypy>=1.14,<2,不是代码质量问题。
文档守卫:41 份无编号冲突。
2026-09-12 11:23:46 +08:00