Skip to content

Commit 7611d42

Browse files
dengzhaofunclaude
andauthored
Add entity system + unified polymorphic reward type (#19)
* Add entity module — RPG entity system with admin dashboard (#19) Phase 1 of the entity system: Schema/Blueprint/Skin/FormationConfig CRUD with full admin dashboard and i18n support. Backend (apps/server): - 8 new tables: entity_schemas, entity_blueprints, entity_blueprint_skins, entity_instances, entity_slot_assignments, entity_formation_configs, entity_formations, entity_action_logs - Flexible schema system: orgs define entity types (hero, weapon, skill) with configurable stats, tags, slots, level/rank/synthesis rules - Tag-based slot compatibility (e.g. warrior heroes only accept warrior weapons) - Service layer with full CRUD + alias conflict detection + cascade deletion - 20 admin REST endpoints with OpenAPI/Zod validation - 32 service tests (all passing against real Neon DB) - Migration 0014 generated and applied Frontend (apps/admin): - Entity dashboard with tabbed view (schemas + formation configs) - Schema create/detail pages with dynamic stat/tag/slot editors - Blueprint create/detail pages with schema-driven forms - Skin create page - Formation config create page - 6 reusable components (SchemaTable, SchemaForm, BlueprintTable, BlueprintForm, SkinTable, FormationConfigTable) - React Query hooks for all CRUD operations - Full i18n (en + zh) with ~75 new message keys - Sidebar navigation entry with Sparkles icon Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Add entity instance management, progression, slots, and formations Phase 2-5 of the entity system — all runtime operations: Instance management: - acquireEntity: create new instances with computed initial stats - listInstances: filter by schemaId/blueprintId (backpack categories) - getInstance: returns instance + equipped slot details - discardEntity: with lock protection and equipped-check - toggleLock: prevent accidental discard/synthesis Progression system: - addExp: direct experience injection (battle rewards) - levelUp: consumes item materials per blueprint.levelUpCosts config - rankUp: consumes materials, advances rank (N→R→SR→SSR) - synthesize: merge N duplicate entities, consuming feeds + materials Stat computation pipeline: - Pure function: base + growth×(level-1) + rank bonuses + equipment + skin - Cached in computedStats JSONB, recomputed on every mutation - Read paths return cached values (zero computation) Slot system: - equip/unequip with schema + tag compatibility validation - $owner.class dynamic tag matching (warrior heroes accept warrior weapons) - Unique constraint: one entity can only be in one slot at a time Formations: - updateFormation: upsert with schema/duplicate validation - listFormations: per-config, per-user Client API: 14 new endpoints under /api/client/entity/ Tests: 55/55 passing (32 Phase 1 + 23 Phase 2-5) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Unify reward system with polymorphic RewardEntry type Replace all ItemEntry[] JSONB columns with RewardEntry[] across the entire platform. RewardEntry uses a polymorphic { type, id, count } shape instead of the old { definitionId, quantity }. This means a single column handles all reward types — items, entities, and future types (points, external goods) — without ever needing to add new columns. Changes: - Add src/lib/rewards.ts with RewardEntry type, grantRewards() and deductCosts() dispatch functions, filterByType() helper - Update all 8 schema files (shop, lottery, mail, exchange, check-in, collection, cdkey, entity) to use RewardEntry[] type annotations - Update itemService.grantItems/deductItems to accept both RewardEntry { id, count } and legacy { definitionId, quantity } - Update all 8 module services, validators, routes, and types to use the new RewardEntry format - Update all test files with new { type: "item", id, count } format - No schema migration needed (JSONB columns, type change is TS-only) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 01ddfe9 commit 7611d42

74 files changed

Lines changed: 18629 additions & 225 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/admin/messages/en.json

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,10 @@
114114
"common_type": "Type",
115115
"common_icon": "Icon",
116116
"common_sort_order": "Sort Order",
117+
"common_required": "Required",
118+
"common_optional": "optional",
119+
"common_yes": "Yes",
120+
"common_no": "No",
117121
"common_dash": "",
118122
"dashboard_title": "Dashboard",
119123
"dashboard_welcome": "Welcome, {name}",
@@ -758,5 +762,78 @@
758762
"gift_package_deleted": "Gift package deleted",
759763
"gift_no_settings": "No gift settings configured yet.",
760764
"gift_no_packages": "No gift packages yet.",
761-
"gift_items_count": "Items"
765+
"gift_items_count": "Items",
766+
767+
"nav_entity": "Entity",
768+
"entity_title": "Entity System",
769+
"entity_schemas": "Entity Schemas",
770+
"entity_blueprints": "Blueprints",
771+
"entity_skins": "Skins",
772+
"entity_formations": "Formation Configs",
773+
"entity_new_schema": "New Schema",
774+
"entity_new_blueprint": "New Blueprint",
775+
"entity_new_skin": "New Skin",
776+
"entity_new_formation": "New Formation Config",
777+
"entity_schema_created": "Schema created successfully",
778+
"entity_schema_updated": "Schema updated",
779+
"entity_schema_deleted": "Schema deleted",
780+
"entity_blueprint_created": "Blueprint created successfully",
781+
"entity_blueprint_updated": "Blueprint updated",
782+
"entity_blueprint_deleted": "Blueprint deleted",
783+
"entity_skin_created": "Skin created successfully",
784+
"entity_skin_updated": "Skin updated",
785+
"entity_skin_deleted": "Skin deleted",
786+
"entity_formation_created": "Formation config created successfully",
787+
"entity_formation_updated": "Formation config updated",
788+
"entity_formation_deleted": "Formation config deleted",
789+
"entity_no_schemas": "No entity schemas yet.",
790+
"entity_no_blueprints": "No blueprints yet.",
791+
"entity_no_skins": "No skins yet.",
792+
"entity_no_formations": "No formation configs yet.",
793+
"entity_stat_definitions": "Stat Definitions",
794+
"entity_tag_definitions": "Tag Definitions",
795+
"entity_slot_definitions": "Slot Definitions",
796+
"entity_level_config": "Level Config",
797+
"entity_rank_config": "Rank Config",
798+
"entity_synthesis_config": "Synthesis Config",
799+
"entity_base_stats": "Base Stats",
800+
"entity_stat_growth": "Stat Growth",
801+
"entity_level_up_costs": "Level Up Costs",
802+
"entity_rank_up_costs": "Rank Up Costs",
803+
"entity_synthesis_cost": "Synthesis Cost",
804+
"entity_tags": "Tags",
805+
"entity_assets": "Assets",
806+
"entity_rarity": "Rarity",
807+
"entity_max_level": "Max Level",
808+
"entity_max_formations": "Max Formations",
809+
"entity_max_slots": "Max Slots Per Formation",
810+
"entity_accepts_schemas": "Accepted Schemas",
811+
"entity_allow_duplicate_blueprints": "Allow Duplicate Blueprints",
812+
"entity_stat_bonuses": "Stat Bonuses",
813+
"entity_is_default": "Default Skin",
814+
"entity_delete_schema_title": "Delete Schema?",
815+
"entity_delete_schema_desc": "This will permanently delete the schema and all associated blueprints, skins, and instances.",
816+
"entity_delete_blueprint_title": "Delete Blueprint?",
817+
"entity_delete_blueprint_desc": "This will permanently delete the blueprint and all associated skins and instances.",
818+
"entity_delete_skin_title": "Delete Skin?",
819+
"entity_delete_skin_desc": "This will permanently delete this skin.",
820+
"entity_delete_formation_title": "Delete Formation Config?",
821+
"entity_delete_formation_desc": "This will permanently delete this formation config and all player formations using it.",
822+
"entity_enabled": "Enabled",
823+
"entity_disabled": "Disabled",
824+
"entity_same_blueprint_required": "Same Blueprint Required",
825+
"entity_input_count": "Input Count",
826+
"entity_ranks": "Ranks",
827+
"entity_key": "Key",
828+
"entity_label": "Label",
829+
"entity_type": "Type",
830+
"entity_default_value": "Default Value",
831+
"entity_values": "Values",
832+
"entity_accepted_schema_ids": "Accepted Schema IDs",
833+
"entity_accept_tags": "Accept Tags",
834+
"entity_max_count": "Max Count",
835+
"entity_add_stat": "Add Stat",
836+
"entity_add_tag": "Add Tag",
837+
"entity_add_slot": "Add Slot",
838+
"entity_add_rank": "Add Rank"
762839
}

apps/admin/messages/zh.json

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,10 @@
114114
"common_type": "类型",
115115
"common_icon": "图标",
116116
"common_sort_order": "排序",
117+
"common_required": "必填",
118+
"common_optional": "可选",
119+
"common_yes": "",
120+
"common_no": "",
117121
"common_dash": "",
118122
"dashboard_title": "仪表盘",
119123
"dashboard_welcome": "欢迎,{name}",
@@ -758,5 +762,78 @@
758762
"gift_package_deleted": "礼包已删除",
759763
"gift_no_settings": "尚未配置赠礼设置。",
760764
"gift_no_packages": "暂无礼包模板。",
761-
"gift_items_count": "物品数"
765+
"gift_items_count": "物品数",
766+
767+
"nav_entity": "实体",
768+
"entity_title": "实体系统",
769+
"entity_schemas": "实体类型",
770+
"entity_blueprints": "模板",
771+
"entity_skins": "皮肤",
772+
"entity_formations": "编队配置",
773+
"entity_new_schema": "新建类型",
774+
"entity_new_blueprint": "新建模板",
775+
"entity_new_skin": "新建皮肤",
776+
"entity_new_formation": "新建编队配置",
777+
"entity_schema_created": "类型创建成功",
778+
"entity_schema_updated": "类型已更新",
779+
"entity_schema_deleted": "类型已删除",
780+
"entity_blueprint_created": "模板创建成功",
781+
"entity_blueprint_updated": "模板已更新",
782+
"entity_blueprint_deleted": "模板已删除",
783+
"entity_skin_created": "皮肤创建成功",
784+
"entity_skin_updated": "皮肤已更新",
785+
"entity_skin_deleted": "皮肤已删除",
786+
"entity_formation_created": "编队配置创建成功",
787+
"entity_formation_updated": "编队配置已更新",
788+
"entity_formation_deleted": "编队配置已删除",
789+
"entity_no_schemas": "暂无实体类型。",
790+
"entity_no_blueprints": "暂无模板。",
791+
"entity_no_skins": "暂无皮肤。",
792+
"entity_no_formations": "暂无编队配置。",
793+
"entity_stat_definitions": "属性定义",
794+
"entity_tag_definitions": "标签定义",
795+
"entity_slot_definitions": "槽位定义",
796+
"entity_level_config": "等级配置",
797+
"entity_rank_config": "升阶配置",
798+
"entity_synthesis_config": "合成配置",
799+
"entity_base_stats": "基础属性",
800+
"entity_stat_growth": "属性成长",
801+
"entity_level_up_costs": "升级消耗",
802+
"entity_rank_up_costs": "升阶消耗",
803+
"entity_synthesis_cost": "合成消耗",
804+
"entity_tags": "标签",
805+
"entity_assets": "资源",
806+
"entity_rarity": "稀有度",
807+
"entity_max_level": "最大等级",
808+
"entity_max_formations": "最大队伍数",
809+
"entity_max_slots": "每队最大人数",
810+
"entity_accepts_schemas": "接受的类型",
811+
"entity_allow_duplicate_blueprints": "允许同模板",
812+
"entity_stat_bonuses": "属性加成",
813+
"entity_is_default": "默认皮肤",
814+
"entity_delete_schema_title": "删除类型?",
815+
"entity_delete_schema_desc": "此操作将永久删除该类型及其所有关联的模板、皮肤和实例。",
816+
"entity_delete_blueprint_title": "删除模板?",
817+
"entity_delete_blueprint_desc": "此操作将永久删除该模板及其所有关联的皮肤和实例。",
818+
"entity_delete_skin_title": "删除皮肤?",
819+
"entity_delete_skin_desc": "此操作将永久删除此皮肤。",
820+
"entity_delete_formation_title": "删除编队配置?",
821+
"entity_delete_formation_desc": "此操作将永久删除此编队配置及所有使用它的玩家编队数据。",
822+
"entity_enabled": "已启用",
823+
"entity_disabled": "已禁用",
824+
"entity_same_blueprint_required": "需要同模板",
825+
"entity_input_count": "输入数量",
826+
"entity_ranks": "阶位",
827+
"entity_key": "键名",
828+
"entity_label": "标签",
829+
"entity_type": "类型",
830+
"entity_default_value": "默认值",
831+
"entity_values": "可选值",
832+
"entity_accepted_schema_ids": "接受的类型ID",
833+
"entity_accept_tags": "接受的标签",
834+
"entity_max_count": "最大数量",
835+
"entity_add_stat": "添加属性",
836+
"entity_add_tag": "添加标签",
837+
"entity_add_slot": "添加槽位",
838+
"entity_add_rank": "添加阶位"
762839
}

apps/admin/src/components/AppSidebar.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
Package,
1414
Shield,
1515
ShoppingCart,
16+
Sparkles,
1617
Swords,
1718
Ticket,
1819
Users,
@@ -39,6 +40,7 @@ function getNavItems() {
3940
{ title: m.nav_dashboard(), to: "/dashboard" as const, icon: LayoutDashboard },
4041
{ title: m.nav_checkin(), to: "/check-in" as const, icon: CalendarCheck },
4142
{ title: m.nav_item(), to: "/item" as const, icon: Package },
43+
{ title: m.nav_entity(), to: "/entity" as const, icon: Sparkles },
4244
{ title: m.nav_exchange(), to: "/exchange" as const, icon: ArrowLeftRight },
4345
{ title: m.nav_cdkey(), to: "/cdkey" as const, icon: Ticket },
4446
{ title: m.nav_shop(), to: "/shop" as const, icon: ShoppingCart },

0 commit comments

Comments
 (0)