entity/skill/SKILL.md

4.6 KiB
Raw Blame History

name description
entity 实体管理模块W-03——实体表entityCRUD、列表查询、实体文件导入entity_import 记录),通过 load_entity() 挂载。

entity 实体管理模块

概述

元景项目 W-03 实体管理管理实体entity数据。实体归属于场景scene_id→scene.id逻辑关联不加外键与世界world_id→world.id。提供实体表 CRUD、分页列表查询、CSV/JSON 文件导入(两阶段校验+批量插入+失败回滚)。

数据模型

说明
entity 实体表。entity_code 唯一索引;entity_type/status 引用 appcodes_kv 字典;scene_id/world_id 为逻辑外键codes 段,无物理外键);import_id 标记导入批次;org_id 机构隔离
entity_import 导入批次记录(系统生成,只读)。import_no 唯一;status ∈ importing/success/fail
entity_import_log 导入逐行明细(系统生成,只读)。result 1=成功 0=失败

编码字典init/data.json 幂等落库 appcodes/appcodes_kv

  • entity_typecharacter/prop/vehicle/building/faction/other
  • common_status1=启用 / 0=停用
  • import_statusimporting/success/fail

关键接口REST 统一前缀 /api/*

接口 方法 说明 返回
/entity/api/entity_list.dspy GET/POST 分页列表keyword/entity_type/status/scene_id 过滤) {list, total, page, rows}
/entity/api/entity_create.dspy POST 新增实体(校验失败 100% 拦截不落库) {success, data:{id}}
/entity/api/entity_update.dspy POST 更新实体(校验+存在性检查) {success, data:{id}}
/entity/api/entity_delete.dspy POST 删除实体 {success, data:{id}}
/entity/api/entity_get.dspy GET/POST 实体详情 {success, data}
/entity/api/entity_import.dspy POST 文件导入file 域 / file_content {success, data:{import_id,total,success,fail}}
/entity/api/entity_import_guard.dspy POST 导入预校验(干跑,只校验不落库) {valid, errors, total}
/entity/api/entity_import_list.dspy GET/POST 导入记录只读分页 {list, total, page, rows}

错误结构统一:{code, message, field, detail}。分页结构统一:{list, total}

导入处理逻辑

  1. 解析CSV首行表头字段 entity_code/entity_name/entity_type/scene_id/world_id/description/status/sort_no或 JSON 数组。
  2. 全量校验(第一阶段,不落库):编码必填/长度/文件内唯一/库内唯一;名称必填;类型/状态字典白名单scene_id/world_id 存在性。任一行非法 → 整批拦截,只写失败批次+逐行日志,不导入任何实体。
  3. 批量插入(第二阶段):同批实体以同一 import_id 标记插入;任一行异常 → 按 import_id 补偿 DELETE 已插入实体,保证失败回滚无脏数据。
  4. 成功/失败均写 entity_import 批次与 entity_import_log 明细。

陷阱

  • sor.I() 只用于表结构:插入用 sor.C(table, ns),不要 sor.I(table, data)I 只收 1 参)。
  • sor.U() 只收 2 参sor.U('entity', {**upd, 'id': id})id 必须在 data 内;仅主键字段时先校验非空字段。
  • dspy 中禁 import:全部函数经 load_entity() 注册为全局(create_entity/update_entity/delete_entity/get_entity/list_entities/import_entities/entity_import_guard/reject_import_writedspy 直接调用;__init__.py 必须导出 init.py 的 async 函数,否则 NameError。
  • 取库名不硬编码.py 用 get_sor_context(ServerEnv(), 'entity')(内部解析 get_module_dbname('entity').dspy 用 get_sor_context(request._run_ns, 'entity')。禁止写 DBNAME='...'
  • 请求上下文.py 内取用户/机构用 request._run_nsget_user/get_userorgid不要用 ServerEnv()(进程级单例)。
  • _text 后缀字段:表格提交会带 xxx_text_clean_ns 会剔除后再写库。
  • curDateString()sqlor 不自动写时间戳,插入必须显式 created_at,否则记录静默丢失。
  • 删除校验entity 可能被 world_snapshot 等引用,删除前可扩展引用检查(当前实现仅校验存在性)。

依赖

  • 基础sqlor、ahserver、appPublic
  • 数据依赖(逻辑关联,不物理外键):scenescene.idworldworld.id——codes 段引用,表缺失时仅下拉不渲染,不影响 CRUD
  • 字典appbase 的 appcodes / appcodes_kv
  • world_snapshotW-03 快照)聚合读取