--- name: entity description: 实体管理模块(W-03)——实体表(entity)CRUD、列表查询、实体文件导入(entity_import 记录),通过 load_entity() 挂载。 --- # entity 模块 ## 概述 实体管理模块,提供实体表(entity)增删改查与文件导入(entity_import 记录表)。依赖 world、scene 模块(逻辑关联,不加物理外键)。 ## 数据模型 ### 表 `entity`(models/entity.json) | 字段 | 类型 | 说明 | |------|------|------| | id | str(32) | 主键 | | world_id | str(32) | 所属世界(→world.id 逻辑关联) | | scene_id | str(32) | 所属场景(→scene.id 逻辑关联,codes 段配置,不加物理外键) | | name | str(255) | 实体名称 | | code | str(64) | 实体编码(唯一,idx_entity_code) | | entity_type | str(16) | 实体类型(appcodes entity_type) | | status | str(16) | 状态(appcodes entity_status) | | attributes_json | text | 属性 JSON | | created_at | timestamp | 创建时间 | | updated_at | timestamp | 更新时间 | - 主键 `["id"]`;唯一索引 `idx_entity_code(code)`;索引 `idx_entity_world(world_id)`、`idx_entity_scene(scene_id)` - codes:world_id → world(id/name)、scene_id → scene(id/name)、entity_type → appcodes_kv(parentid='entity_type')、status → appcodes_kv(parentid='entity_status') ### 表 `entity_import`(models/entity_import.json,只读) | 字段 | 类型 | 说明 | |------|------|------| | id | str(32) | 主键 | | world_id | str(32) | 目标世界 | | scene_id | str(32) | 目标场景 | | file_name | str(255) | 导入文件名 | | total / success / fail | int | 总数 / 成功 / 失败(默认 0) | | status | str(16) | 导入状态(appcodes import_status:0 进行中/1 成功/2 失败) | | created_at | timestamp | 导入时间 | - 主键 `["id"]`;索引 `idx_entity_import_world(world_id)`;**只读记录,不可手工增删改** ## 关键接口(load_entity() 挂载到 ServerEnv) - `list_entities(params)` → `{list, total}`(sqlPaging 分页,支持 world_id/scene_id/entity_type/status/name/code 过滤 + sort/order) - `get_entity({id})` → `{data}` 或错误 - `create_entity(ns)` → 校验 + world/scene 存在校验 + 自动 id/created_at/updated_at;返回 `{success, id}` - `update_entity(ns)` → 写 updated_at、剔除 `_text` 后缀、world/scene 校验 - `delete_entity({id})` → `{success, id}` - `import_entities({world_id, scene_id, file_name, rows})` → `{success, total, success_count, fail, file_name}` (**事务批量**:先全量解析+校验(含文件内编码查重),再逐条插入;任一条失败整批回滚,无脏数据;success_count 避免与布尔 success 冲突) - `parse_entity_file(content, filename)` → `{rows: [...]}`(JSON 数组 / JSON Lines / CSV 表头) - `get_world_options()` / `get_scene_options()` / `get_entity_type_options()` / `get_entity_status_options()` / `get_import_status_options()` → `[{value, text}]` ### wwwroot/api/*.dspy(REST 统一前缀 /api/*,宿主挂载后为 /entity/api/*.dspy) - `entity_list.dspy`(分页 `{list,total}`)、`entity_get.dspy`、`entity_create.dspy`、`entity_update.dspy`、`entity_delete.dspy` - `entity_import.dspy`(world_id + file → parse_entity_file → import_entities) - `get_search_world_id.dspy` / `get_search_scene_id.dspy` / `get_search_entity_type.dspy` / `get_search_status.dspy` / `get_search_import_status.dspy`(字典下拉) ### 错误结构(统一) `{code, message, field, detail}`;code:PARAM_REQUIRED / FIELD_REQUIRED / FIELD_TOO_LONG / WORLD_NOT_FOUND / SCENE_NOT_FOUND / DUPLICATE_CODE / PARSE_ERROR / INVALID_JSON / DB_ERROR / NOT_FOUND。 非法输入 100% 拦截不落库。 ## 陷阱 - 库名禁止硬编码:.py 用 `ServerEnv().get_module_dbname("entity")`,.dspy 用 `get_module_dbname("entity")`(全局);world/scene 表分别在 world/scene 模块库(`get_module_dbname("world"/"scene")`),appcodes 在 `get_module_dbname("appbase")` - dspy 无 import(json/get_sor_context/debug/params_kw 均预加载全局);函数经 load_entity() 注册后为全局,直接调用 - `create_entity` / `import_entities` 必须显式设置 `created_at = curDateString()`,否则 sor.C 静默丢记录 - `entity_import` 记录 total/success/fail 必须为 int - `import_entities` 返回用 `success_count`(避免与布尔 `success` 键冲突) - 三处同步注册:entity/__init__.py 导出 ← entity/init.py 实现 ← load_entity() 注册(含复数别名 create_entities/update_entities/delete_entities) - 返回 `{list,total}` 分页用 `sor.sqlPaging`,不要硬编码 LIMIT/OFFSET - CRUD json `new_data_url` 指向自定义 `api/entity_create.dspy`;entity_import 为只读列表,无 editable ## 依赖 - world(world_id 逻辑关联 + 世界下拉)、scene(scene_id 逻辑关联 + 场景下拉)、appbase(appcodes 字典)、rbac(权限)