feat(db): moments_post 表+upsert(朋友圈实体,PK=云客data.id,create_time统一ms)
- MomentsPost 模型:moments_post 表,PK=id(云客 getFriendsterInfo data.id), wechat_id/create_time 建索引,praise/comment/image 存 JSON 列 - 迁移 524da8d2d597(手写 create_table,已应用到 smoke.db 并在临时 SQLite 全链路 upgrade/downgrade 验证) - repos.upsert_moments_posts:批量幂等 upsert;附带修 _build_column_values 对 JSON 列表的误 join(标量列 join 保持不变,dept_route 旧测试仍过) - 修复 3 个 chat_group 旧测试缺 owner_account_id(2026-08-24 复合主键遗留) - 全套 430 测试通过 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -28,6 +28,7 @@ from app.db.models import (
|
||||
Department,
|
||||
GroupMember,
|
||||
Message,
|
||||
MomentsPost,
|
||||
PhoneCall,
|
||||
Sms,
|
||||
Staff,
|
||||
@@ -42,6 +43,7 @@ from app.storage.repos import (
|
||||
upsert_customers,
|
||||
upsert_department,
|
||||
upsert_messages,
|
||||
upsert_moments_posts,
|
||||
upsert_phone_call,
|
||||
upsert_sms,
|
||||
upsert_staff,
|
||||
@@ -820,6 +822,7 @@ async def test_upsert_chat_group_reupsert_updates_group_keeps_members_unique():
|
||||
rec1 = {
|
||||
"room_id": "98765432109@chatroom",
|
||||
"channel": "wechat",
|
||||
"owner_account_id": "wxid_owner_re",
|
||||
"room_name": "Old Name",
|
||||
"member_count": 2,
|
||||
"member_ids": "wxid_m1;wxid_m2",
|
||||
@@ -877,6 +880,7 @@ async def test_upsert_chat_group_drops_empty_member_id():
|
||||
rec = {
|
||||
"room_id": "55555555555@chatroom",
|
||||
"channel": "wechat",
|
||||
"owner_account_id": "wxid_owner_empty",
|
||||
# Empty string sneaks through; repo must skip it.
|
||||
"member_ids_list": ["", "wxid_only", ""],
|
||||
}
|
||||
@@ -905,6 +909,7 @@ async def test_upsert_chat_group_with_no_members_writes_group_only():
|
||||
rec = {
|
||||
"room_id": "empty@chatroom",
|
||||
"channel": "wechat",
|
||||
"owner_account_id": "wxid_owner_none",
|
||||
"room_name": "No Members Yet",
|
||||
"member_ids_list": [],
|
||||
}
|
||||
@@ -1402,3 +1407,55 @@ async def test_upsert_sms_coerces_direction_and_create_time():
|
||||
assert fetched.content == "hi"
|
||||
finally:
|
||||
await engine.dispose()
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# MomentsPost upsert (员工聊天窗口 Phase 1)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
async def test_upsert_moments_posts_idempotent_keeps_json_lists_and_coerces_create_time():
|
||||
"""MomentsPost batch upsert: idempotent on PK ``id``; JSON list columns
|
||||
(praise_json / comment_json / image_urls) stay real lists (NOT joined into
|
||||
";..." strings by the scalar-column list coercion); numeric-str
|
||||
create_time coerces to ms-epoch int.
|
||||
"""
|
||||
engine = await _make_engine()
|
||||
try:
|
||||
SessionLocal = async_sessionmaker(engine, expire_on_commit=False)
|
||||
async with SessionLocal() as session:
|
||||
rec1 = {
|
||||
"id": "69e082a3c3524a7fe3ac53b4",
|
||||
"wechat_id": "wxid_x",
|
||||
"sns_id": "-3546042415332912522",
|
||||
"author_name": "Yoyo",
|
||||
"author_wxid": "wxid_x",
|
||||
"content": "v1 content",
|
||||
"type_raw": "1",
|
||||
"create_time": 1776302051000, # ms epoch int
|
||||
"praise_json": [{"name": "E**U"}],
|
||||
"comment_json": [],
|
||||
"image_urls": ["https://wx.oss.example/a.jpg"],
|
||||
}
|
||||
# Same PK again: later write wins, create_time arrives as a
|
||||
# numeric string (defensive coercion path).
|
||||
rec2 = dict(rec1, content="v2 content", create_time="1776302051999")
|
||||
count = await upsert_moments_posts(session, [rec1, rec2])
|
||||
await session.commit()
|
||||
|
||||
assert count == 2 # returns input length
|
||||
rows = (
|
||||
await session.execute(
|
||||
sa.select(MomentsPost).execution_options(populate_existing=True)
|
||||
)
|
||||
).scalars().all()
|
||||
assert len(rows) == 1 # same PK twice -> ONE row
|
||||
assert rows[0].content == "v2 content" # later record wins
|
||||
assert rows[0].create_time == 1776302051999 # str -> int
|
||||
# JSON list columns preserved verbatim as lists.
|
||||
assert rows[0].praise_json == [{"name": "E**U"}]
|
||||
assert rows[0].comment_json == []
|
||||
assert rows[0].image_urls == ["https://wx.oss.example/a.jpg"]
|
||||
assert rows[0].type_raw == "1" # MomentsPost type_raw is a string code
|
||||
finally:
|
||||
await engine.dispose()
|
||||
|
||||
Reference in New Issue
Block a user