refactor(bootstrap): migrate basic puzzles to seed files
Replaces GlobalsImporter's ImportPuzzleGroups/Puzzles/Missions methods (plus the DeriveTargetPuzzleGroupId regex helper) with a dedicated PuzzleImporter that reads three flat seed JSONs (puzzle-groups, puzzles, puzzle-missions) produced by the Python extractor. Groups run before puzzles to satisfy the FK; missions upsert by sequential id. Wired into Program.cs and SVSimTestFactory after PaymentItemImporter so existing GlobalsImporterPuzzleTests continue to pass unchanged via SeedGlobalsAsync. The original prod-capture JSONs are deleted now that the seeds are authoritative. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
{"data_headers":{"sid":"079f239bb83de281ebc6b2f68dbb2cd11779683743","short_udid":411054851,"viewer_id":906243102,"servertime":1779683743,"result_code":1},"data":[{"mission_name":"Clear all Dragoncraft and Portalcraft puzzles puzzles in the Special Round","require_number":"2","campaign_commence_time":1725670800,"reward_list":[{"reward_type":"4","reward_detail_id":"90001","reward_number":"1"}],"order_id":"5","total_count":"0","is_achieved":false},{"mission_name":"Clear all Forestcraft, Shadowcraft and Bloodcraft puzzles in the Special Round","require_number":"3","campaign_commence_time":1722646800,"reward_list":[{"reward_type":"4","reward_detail_id":"90001","reward_number":"1"}],"order_id":"4","total_count":"0","is_achieved":false},{"mission_name":"Clear all Swordcraft, Runecraft and Havencraft puzzles in the Special Round","require_number":"3","campaign_commence_time":1720227600,"reward_list":[{"reward_type":"4","reward_detail_id":"90001","reward_number":"1"}],"order_id":"3","total_count":"0","is_achieved":false},{"mission_name":"Clear all Special Round puzzles","require_number":"8","campaign_commence_time":1720227600,"reward_list":[{"reward_type":"7","reward_detail_id":"400004315","reward_number":"1"}],"order_id":"2","total_count":"0","is_achieved":false},{"mission_name":"Clear all Round 15 puzzles","require_number":"3","campaign_commence_time":1716598800,"reward_list":[{"reward_type":"7","reward_detail_id":"400004314","reward_number":"1"}],"order_id":"1","total_count":"0","is_achieved":false},{"mission_name":"Clear all Round 14 puzzles","require_number":"3","campaign_commence_time":1711760400,"reward_list":[{"reward_type":"6","reward_detail_id":"3065004","reward_number":"1"}],"order_id":"1","total_count":"0","is_achieved":false},{"mission_name":"Clear all Round 13 puzzles","require_number":"3","campaign_commence_time":1708736400,"reward_list":[{"reward_type":"7","reward_detail_id":"400004313","reward_number":"1"}],"order_id":"1","total_count":"0","is_achieved":false},{"mission_name":"Clear all Round 12 puzzles","require_number":"3","campaign_commence_time":1703898000,"reward_list":[{"reward_type":"6","reward_detail_id":"3074009","reward_number":"1"}],"order_id":"1","total_count":"0","is_achieved":false},{"mission_name":"Clear all Round 11 puzzles","require_number":"3","campaign_commence_time":1700269200,"reward_list":[{"reward_type":"6","reward_detail_id":"3074008","reward_number":"1"}],"order_id":"1","total_count":"0","is_achieved":false},{"mission_name":"Clear all Round 10 puzzles","require_number":"3","campaign_commence_time":1692406800,"reward_list":[{"reward_type":"6","reward_detail_id":"3074007","reward_number":"1"}],"order_id":"1","total_count":"0","is_achieved":false},{"mission_name":"Clear all Round 9 puzzles","require_number":"3","campaign_commence_time":1688173200,"reward_list":[{"reward_type":"6","reward_detail_id":"3074006","reward_number":"1"}],"order_id":"1","total_count":"0","is_achieved":false},{"mission_name":"Clear all Round 8 puzzles","require_number":"3","campaign_commence_time":1684544400,"reward_list":[{"reward_type":"6","reward_detail_id":"3074005","reward_number":"1"}],"order_id":"1","total_count":"0","is_achieved":false},{"mission_name":"Clear all Round 7 puzzles","require_number":"3","campaign_commence_time":1677286800,"reward_list":[{"reward_type":"6","reward_detail_id":"3074004","reward_number":"1"}],"order_id":"1","total_count":"0","is_achieved":false},{"mission_name":"Clear all Round 6 puzzles","require_number":"3","campaign_commence_time":1672448400,"reward_list":[{"reward_type":"6","reward_detail_id":"3074003","reward_number":"1"}],"order_id":"1","total_count":"0","is_achieved":false},{"mission_name":"Clear all Round 5 puzzles","require_number":"3","campaign_commence_time":1669424400,"reward_list":[{"reward_type":"6","reward_detail_id":"3074002","reward_number":"1"}],"order_id":"1","total_count":"0","is_achieved":false},{"mission_name":"Clear all Round 4 puzzles","require_number":"3","campaign_commence_time":1660959000,"reward_list":[{"reward_type":"6","reward_detail_id":"3074001","reward_number":"1"}],"order_id":"1","total_count":"0","is_achieved":false},{"mission_name":"Clear all Round 3 puzzles","require_number":"3","campaign_commence_time":1656725400,"reward_list":[{"reward_type":"7","reward_detail_id":"400004105","reward_number":"1"}],"order_id":"1","total_count":"0","is_achieved":false},{"mission_name":"Clear all Round 2 puzzles","require_number":"3","campaign_commence_time":1653096600,"reward_list":[{"reward_type":"7","reward_detail_id":"400004104","reward_number":"1"}],"order_id":"1","total_count":"0","is_achieved":false},{"mission_name":"Clear all Round 1 puzzles","require_number":"3","campaign_commence_time":1651282200,"reward_list":[{"reward_type":"10","reward_detail_id":"3704","reward_number":"1"}],"order_id":"1","total_count":"3","is_achieved":true}]}
|
||||
305
SVSim.Bootstrap/Data/seeds/puzzle-groups.json
Normal file
305
SVSim.Bootstrap/Data/seeds/puzzle-groups.json
Normal file
@@ -0,0 +1,305 @@
|
||||
[
|
||||
{
|
||||
"id": 316,
|
||||
"basic_title_text_id": "Puzzle_QuestSelect_0316",
|
||||
"puzzle_chara_id": 3704,
|
||||
"chara_id": 3704,
|
||||
"sort_type": 1,
|
||||
"difficulty_name_list": {
|
||||
"Expert": "2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 315,
|
||||
"basic_title_text_id": "Puzzle_QuestSelect_0315",
|
||||
"puzzle_chara_id": 3704,
|
||||
"chara_id": 3704,
|
||||
"sort_type": 1,
|
||||
"difficulty_name_list": {
|
||||
"Beginner": "0",
|
||||
"Experienced": "1",
|
||||
"Expert": "2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 314,
|
||||
"basic_title_text_id": "Puzzle_QuestSelect_0314",
|
||||
"puzzle_chara_id": 3704,
|
||||
"chara_id": 3704,
|
||||
"sort_type": 1,
|
||||
"difficulty_name_list": {
|
||||
"Beginner": "0",
|
||||
"Experienced": "1",
|
||||
"Expert": "2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 313,
|
||||
"basic_title_text_id": "Puzzle_QuestSelect_0313",
|
||||
"puzzle_chara_id": 3704,
|
||||
"chara_id": 3704,
|
||||
"sort_type": 1,
|
||||
"difficulty_name_list": {
|
||||
"Beginner": "0",
|
||||
"Experienced": "1",
|
||||
"Expert": "2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 312,
|
||||
"basic_title_text_id": "Puzzle_QuestSelect_0312",
|
||||
"puzzle_chara_id": 3704,
|
||||
"chara_id": 3704,
|
||||
"sort_type": 1,
|
||||
"difficulty_name_list": {
|
||||
"Beginner": "0",
|
||||
"Experienced": "1",
|
||||
"Expert": "2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 311,
|
||||
"basic_title_text_id": "Puzzle_QuestSelect_0311",
|
||||
"puzzle_chara_id": 3704,
|
||||
"chara_id": 3704,
|
||||
"sort_type": 1,
|
||||
"difficulty_name_list": {
|
||||
"Beginner": "0",
|
||||
"Experienced": "1",
|
||||
"Expert": "2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 310,
|
||||
"basic_title_text_id": "Puzzle_QuestSelect_0310",
|
||||
"puzzle_chara_id": 3704,
|
||||
"chara_id": 3704,
|
||||
"sort_type": 1,
|
||||
"difficulty_name_list": {
|
||||
"Beginner": "0",
|
||||
"Experienced": "1",
|
||||
"Expert": "2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 309,
|
||||
"basic_title_text_id": "Puzzle_QuestSelect_0309",
|
||||
"puzzle_chara_id": 3704,
|
||||
"chara_id": 3704,
|
||||
"sort_type": 1,
|
||||
"difficulty_name_list": {
|
||||
"Beginner": "0",
|
||||
"Experienced": "1",
|
||||
"Expert": "2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 308,
|
||||
"basic_title_text_id": "Puzzle_QuestSelect_0308",
|
||||
"puzzle_chara_id": 3704,
|
||||
"chara_id": 3704,
|
||||
"sort_type": 1,
|
||||
"difficulty_name_list": {
|
||||
"Beginner": "0",
|
||||
"Experienced": "1",
|
||||
"Expert": "2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 307,
|
||||
"basic_title_text_id": "Puzzle_QuestSelect_0307",
|
||||
"puzzle_chara_id": 3704,
|
||||
"chara_id": 3704,
|
||||
"sort_type": 1,
|
||||
"difficulty_name_list": {
|
||||
"Beginner": "0",
|
||||
"Experienced": "1",
|
||||
"Expert": "2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 306,
|
||||
"basic_title_text_id": "Puzzle_QuestSelect_0306",
|
||||
"puzzle_chara_id": 3704,
|
||||
"chara_id": 3704,
|
||||
"sort_type": 1,
|
||||
"difficulty_name_list": {
|
||||
"Beginner": "0",
|
||||
"Experienced": "1",
|
||||
"Expert": "2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 305,
|
||||
"basic_title_text_id": "Puzzle_QuestSelect_0305",
|
||||
"puzzle_chara_id": 3704,
|
||||
"chara_id": 3704,
|
||||
"sort_type": 1,
|
||||
"difficulty_name_list": {
|
||||
"Beginner": "0",
|
||||
"Experienced": "1",
|
||||
"Expert": "2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 304,
|
||||
"basic_title_text_id": "Puzzle_QuestSelect_0304",
|
||||
"puzzle_chara_id": 3704,
|
||||
"chara_id": 3704,
|
||||
"sort_type": 1,
|
||||
"difficulty_name_list": {
|
||||
"Beginner": "0",
|
||||
"Experienced": "1",
|
||||
"Expert": "2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 303,
|
||||
"basic_title_text_id": "Puzzle_QuestSelect_0303",
|
||||
"puzzle_chara_id": 3704,
|
||||
"chara_id": 3704,
|
||||
"sort_type": 1,
|
||||
"difficulty_name_list": {
|
||||
"Beginner": "0",
|
||||
"Experienced": "1",
|
||||
"Expert": "2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 302,
|
||||
"basic_title_text_id": "Puzzle_QuestSelect_0302",
|
||||
"puzzle_chara_id": 3704,
|
||||
"chara_id": 3704,
|
||||
"sort_type": 1,
|
||||
"difficulty_name_list": {
|
||||
"Beginner": "0",
|
||||
"Experienced": "1",
|
||||
"Expert": "2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 301,
|
||||
"basic_title_text_id": "Puzzle_QuestSelect_0301",
|
||||
"puzzle_chara_id": 3704,
|
||||
"chara_id": 3704,
|
||||
"sort_type": 1,
|
||||
"difficulty_name_list": {
|
||||
"Beginner": "0",
|
||||
"Experienced": "1",
|
||||
"Expert": "2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"basic_title_text_id": "Puzzle_QuestSelect_0109",
|
||||
"puzzle_chara_id": 600090,
|
||||
"chara_id": 600090,
|
||||
"sort_type": 2,
|
||||
"difficulty_name_list": {
|
||||
"Beginner": "0",
|
||||
"Experienced": "1",
|
||||
"Expert": "2",
|
||||
"": "3"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"basic_title_text_id": "Puzzle_QuestSelect_0108",
|
||||
"puzzle_chara_id": 600080,
|
||||
"chara_id": 600080,
|
||||
"sort_type": 2,
|
||||
"difficulty_name_list": {
|
||||
"Beginner": "0",
|
||||
"Experienced": "1",
|
||||
"Expert": "2",
|
||||
"": "3"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"basic_title_text_id": "Puzzle_QuestSelect_0107",
|
||||
"puzzle_chara_id": 600070,
|
||||
"chara_id": 600070,
|
||||
"sort_type": 2,
|
||||
"difficulty_name_list": {
|
||||
"Beginner": "0",
|
||||
"Experienced": "1",
|
||||
"Expert": "2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"basic_title_text_id": "Puzzle_QuestSelect_0106",
|
||||
"puzzle_chara_id": 600060,
|
||||
"chara_id": 600060,
|
||||
"sort_type": 2,
|
||||
"difficulty_name_list": {
|
||||
"Beginner": "0",
|
||||
"Experienced": "1",
|
||||
"Expert": "2",
|
||||
"": "3"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"basic_title_text_id": "Puzzle_QuestSelect_0105",
|
||||
"puzzle_chara_id": 3801,
|
||||
"chara_id": 3801,
|
||||
"sort_type": 2,
|
||||
"difficulty_name_list": {
|
||||
"Beginner": "0",
|
||||
"Experienced": "1",
|
||||
"Expert": "2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"basic_title_text_id": "Puzzle_QuestSelect_0104",
|
||||
"puzzle_chara_id": 3603,
|
||||
"chara_id": 3603,
|
||||
"sort_type": 2,
|
||||
"difficulty_name_list": {
|
||||
"Beginner": "0",
|
||||
"Experienced": "1",
|
||||
"Expert": "2",
|
||||
"": "3"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"basic_title_text_id": "Puzzle_QuestSelect_0103",
|
||||
"puzzle_chara_id": 3403,
|
||||
"chara_id": 3403,
|
||||
"sort_type": 2,
|
||||
"difficulty_name_list": {
|
||||
"Beginner": "0",
|
||||
"Experienced": "1",
|
||||
"Expert": "2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"basic_title_text_id": "Puzzle_QuestSelect_0102",
|
||||
"puzzle_chara_id": 3208,
|
||||
"chara_id": 2703,
|
||||
"sort_type": 2,
|
||||
"difficulty_name_list": {
|
||||
"Beginner": "0",
|
||||
"Experienced": "1",
|
||||
"Expert": "2",
|
||||
"": "3"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"basic_title_text_id": "Puzzle_QuestSelect_0101",
|
||||
"puzzle_chara_id": 600050,
|
||||
"chara_id": 600050,
|
||||
"sort_type": 2,
|
||||
"difficulty_name_list": {
|
||||
"Beginner": "0",
|
||||
"Experienced": "1",
|
||||
"Expert": "2"
|
||||
}
|
||||
}
|
||||
]
|
||||
230
SVSim.Bootstrap/Data/seeds/puzzle-missions.json
Normal file
230
SVSim.Bootstrap/Data/seeds/puzzle-missions.json
Normal file
@@ -0,0 +1,230 @@
|
||||
[
|
||||
{
|
||||
"id": 1,
|
||||
"mission_name": "Clear all Dragoncraft and Portalcraft puzzles puzzles in the Special Round",
|
||||
"achieved_message": "Mission achieved",
|
||||
"require_number": 2,
|
||||
"campaign_commence_time": 1725670800,
|
||||
"order_id": 5,
|
||||
"reward_type": 4,
|
||||
"reward_detail_id": 90001,
|
||||
"reward_number": 1,
|
||||
"target_puzzle_group_id": null
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"mission_name": "Clear all Forestcraft, Shadowcraft and Bloodcraft puzzles in the Special Round",
|
||||
"achieved_message": "Mission achieved",
|
||||
"require_number": 3,
|
||||
"campaign_commence_time": 1722646800,
|
||||
"order_id": 4,
|
||||
"reward_type": 4,
|
||||
"reward_detail_id": 90001,
|
||||
"reward_number": 1,
|
||||
"target_puzzle_group_id": null
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"mission_name": "Clear all Swordcraft, Runecraft and Havencraft puzzles in the Special Round",
|
||||
"achieved_message": "Mission achieved",
|
||||
"require_number": 3,
|
||||
"campaign_commence_time": 1720227600,
|
||||
"order_id": 3,
|
||||
"reward_type": 4,
|
||||
"reward_detail_id": 90001,
|
||||
"reward_number": 1,
|
||||
"target_puzzle_group_id": null
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"mission_name": "Clear all Special Round puzzles",
|
||||
"achieved_message": "Mission achieved",
|
||||
"require_number": 8,
|
||||
"campaign_commence_time": 1720227600,
|
||||
"order_id": 2,
|
||||
"reward_type": 7,
|
||||
"reward_detail_id": 400004315,
|
||||
"reward_number": 1,
|
||||
"target_puzzle_group_id": null
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"mission_name": "Clear all Round 15 puzzles",
|
||||
"achieved_message": "Cleared all Round 15 puzzles",
|
||||
"require_number": 3,
|
||||
"campaign_commence_time": 1716598800,
|
||||
"order_id": 1,
|
||||
"reward_type": 7,
|
||||
"reward_detail_id": 400004314,
|
||||
"reward_number": 1,
|
||||
"target_puzzle_group_id": 315
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"mission_name": "Clear all Round 14 puzzles",
|
||||
"achieved_message": "Cleared all Round 14 puzzles",
|
||||
"require_number": 3,
|
||||
"campaign_commence_time": 1711760400,
|
||||
"order_id": 1,
|
||||
"reward_type": 6,
|
||||
"reward_detail_id": 3065004,
|
||||
"reward_number": 1,
|
||||
"target_puzzle_group_id": 314
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"mission_name": "Clear all Round 13 puzzles",
|
||||
"achieved_message": "Cleared all Round 13 puzzles",
|
||||
"require_number": 3,
|
||||
"campaign_commence_time": 1708736400,
|
||||
"order_id": 1,
|
||||
"reward_type": 7,
|
||||
"reward_detail_id": 400004313,
|
||||
"reward_number": 1,
|
||||
"target_puzzle_group_id": 313
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"mission_name": "Clear all Round 12 puzzles",
|
||||
"achieved_message": "Cleared all Round 12 puzzles",
|
||||
"require_number": 3,
|
||||
"campaign_commence_time": 1703898000,
|
||||
"order_id": 1,
|
||||
"reward_type": 6,
|
||||
"reward_detail_id": 3074009,
|
||||
"reward_number": 1,
|
||||
"target_puzzle_group_id": 312
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"mission_name": "Clear all Round 11 puzzles",
|
||||
"achieved_message": "Cleared all Round 11 puzzles",
|
||||
"require_number": 3,
|
||||
"campaign_commence_time": 1700269200,
|
||||
"order_id": 1,
|
||||
"reward_type": 6,
|
||||
"reward_detail_id": 3074008,
|
||||
"reward_number": 1,
|
||||
"target_puzzle_group_id": 311
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"mission_name": "Clear all Round 10 puzzles",
|
||||
"achieved_message": "Cleared all Round 10 puzzles",
|
||||
"require_number": 3,
|
||||
"campaign_commence_time": 1692406800,
|
||||
"order_id": 1,
|
||||
"reward_type": 6,
|
||||
"reward_detail_id": 3074007,
|
||||
"reward_number": 1,
|
||||
"target_puzzle_group_id": 310
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"mission_name": "Clear all Round 9 puzzles",
|
||||
"achieved_message": "Cleared all Round 9 puzzles",
|
||||
"require_number": 3,
|
||||
"campaign_commence_time": 1688173200,
|
||||
"order_id": 1,
|
||||
"reward_type": 6,
|
||||
"reward_detail_id": 3074006,
|
||||
"reward_number": 1,
|
||||
"target_puzzle_group_id": 309
|
||||
},
|
||||
{
|
||||
"id": 12,
|
||||
"mission_name": "Clear all Round 8 puzzles",
|
||||
"achieved_message": "Cleared all Round 8 puzzles",
|
||||
"require_number": 3,
|
||||
"campaign_commence_time": 1684544400,
|
||||
"order_id": 1,
|
||||
"reward_type": 6,
|
||||
"reward_detail_id": 3074005,
|
||||
"reward_number": 1,
|
||||
"target_puzzle_group_id": 308
|
||||
},
|
||||
{
|
||||
"id": 13,
|
||||
"mission_name": "Clear all Round 7 puzzles",
|
||||
"achieved_message": "Cleared all Round 7 puzzles",
|
||||
"require_number": 3,
|
||||
"campaign_commence_time": 1677286800,
|
||||
"order_id": 1,
|
||||
"reward_type": 6,
|
||||
"reward_detail_id": 3074004,
|
||||
"reward_number": 1,
|
||||
"target_puzzle_group_id": 307
|
||||
},
|
||||
{
|
||||
"id": 14,
|
||||
"mission_name": "Clear all Round 6 puzzles",
|
||||
"achieved_message": "Cleared all Round 6 puzzles",
|
||||
"require_number": 3,
|
||||
"campaign_commence_time": 1672448400,
|
||||
"order_id": 1,
|
||||
"reward_type": 6,
|
||||
"reward_detail_id": 3074003,
|
||||
"reward_number": 1,
|
||||
"target_puzzle_group_id": 306
|
||||
},
|
||||
{
|
||||
"id": 15,
|
||||
"mission_name": "Clear all Round 5 puzzles",
|
||||
"achieved_message": "Cleared all Round 5 puzzles",
|
||||
"require_number": 3,
|
||||
"campaign_commence_time": 1669424400,
|
||||
"order_id": 1,
|
||||
"reward_type": 6,
|
||||
"reward_detail_id": 3074002,
|
||||
"reward_number": 1,
|
||||
"target_puzzle_group_id": 305
|
||||
},
|
||||
{
|
||||
"id": 16,
|
||||
"mission_name": "Clear all Round 4 puzzles",
|
||||
"achieved_message": "Cleared all Round 4 puzzles",
|
||||
"require_number": 3,
|
||||
"campaign_commence_time": 1660959000,
|
||||
"order_id": 1,
|
||||
"reward_type": 6,
|
||||
"reward_detail_id": 3074001,
|
||||
"reward_number": 1,
|
||||
"target_puzzle_group_id": 304
|
||||
},
|
||||
{
|
||||
"id": 17,
|
||||
"mission_name": "Clear all Round 3 puzzles",
|
||||
"achieved_message": "Cleared all Round 3 puzzles",
|
||||
"require_number": 3,
|
||||
"campaign_commence_time": 1656725400,
|
||||
"order_id": 1,
|
||||
"reward_type": 7,
|
||||
"reward_detail_id": 400004105,
|
||||
"reward_number": 1,
|
||||
"target_puzzle_group_id": 303
|
||||
},
|
||||
{
|
||||
"id": 18,
|
||||
"mission_name": "Clear all Round 2 puzzles",
|
||||
"achieved_message": "Cleared all Round 2 puzzles",
|
||||
"require_number": 3,
|
||||
"campaign_commence_time": 1653096600,
|
||||
"order_id": 1,
|
||||
"reward_type": 7,
|
||||
"reward_detail_id": 400004104,
|
||||
"reward_number": 1,
|
||||
"target_puzzle_group_id": 302
|
||||
},
|
||||
{
|
||||
"id": 19,
|
||||
"mission_name": "Clear all Round 1 puzzles",
|
||||
"achieved_message": "Cleared all Round 1 puzzles",
|
||||
"require_number": 3,
|
||||
"campaign_commence_time": 1651282200,
|
||||
"order_id": 1,
|
||||
"reward_type": 10,
|
||||
"reward_detail_id": 3704,
|
||||
"reward_number": 1,
|
||||
"target_puzzle_group_id": 301
|
||||
}
|
||||
]
|
||||
906
SVSim.Bootstrap/Data/seeds/puzzles.json
Normal file
906
SVSim.Bootstrap/Data/seeds/puzzles.json
Normal file
@@ -0,0 +1,906 @@
|
||||
[
|
||||
{
|
||||
"id": 106,
|
||||
"group_id": 316,
|
||||
"puzzle_difficulty": 2,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 107,
|
||||
"group_id": 316,
|
||||
"puzzle_difficulty": 2,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 108,
|
||||
"group_id": 316,
|
||||
"puzzle_difficulty": 2,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 109,
|
||||
"group_id": 316,
|
||||
"puzzle_difficulty": 2,
|
||||
"is_additional": true,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 110,
|
||||
"group_id": 316,
|
||||
"puzzle_difficulty": 2,
|
||||
"is_additional": true,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 111,
|
||||
"group_id": 316,
|
||||
"puzzle_difficulty": 2,
|
||||
"is_additional": true,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 112,
|
||||
"group_id": 316,
|
||||
"puzzle_difficulty": 2,
|
||||
"is_additional": true,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 113,
|
||||
"group_id": 316,
|
||||
"puzzle_difficulty": 2,
|
||||
"is_additional": true,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 103,
|
||||
"group_id": 315,
|
||||
"puzzle_difficulty": 0,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 104,
|
||||
"group_id": 315,
|
||||
"puzzle_difficulty": 1,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 105,
|
||||
"group_id": 315,
|
||||
"puzzle_difficulty": 2,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 100,
|
||||
"group_id": 314,
|
||||
"puzzle_difficulty": 0,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 101,
|
||||
"group_id": 314,
|
||||
"puzzle_difficulty": 1,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 102,
|
||||
"group_id": 314,
|
||||
"puzzle_difficulty": 2,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 97,
|
||||
"group_id": 313,
|
||||
"puzzle_difficulty": 0,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 98,
|
||||
"group_id": 313,
|
||||
"puzzle_difficulty": 1,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 99,
|
||||
"group_id": 313,
|
||||
"puzzle_difficulty": 2,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 94,
|
||||
"group_id": 312,
|
||||
"puzzle_difficulty": 0,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 95,
|
||||
"group_id": 312,
|
||||
"puzzle_difficulty": 1,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 96,
|
||||
"group_id": 312,
|
||||
"puzzle_difficulty": 2,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 91,
|
||||
"group_id": 311,
|
||||
"puzzle_difficulty": 0,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 92,
|
||||
"group_id": 311,
|
||||
"puzzle_difficulty": 1,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 93,
|
||||
"group_id": 311,
|
||||
"puzzle_difficulty": 2,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 84,
|
||||
"group_id": 310,
|
||||
"puzzle_difficulty": 0,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 85,
|
||||
"group_id": 310,
|
||||
"puzzle_difficulty": 1,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 86,
|
||||
"group_id": 310,
|
||||
"puzzle_difficulty": 2,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 77,
|
||||
"group_id": 309,
|
||||
"puzzle_difficulty": 0,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 78,
|
||||
"group_id": 309,
|
||||
"puzzle_difficulty": 1,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 79,
|
||||
"group_id": 309,
|
||||
"puzzle_difficulty": 2,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 74,
|
||||
"group_id": 308,
|
||||
"puzzle_difficulty": 0,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 75,
|
||||
"group_id": 308,
|
||||
"puzzle_difficulty": 1,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 76,
|
||||
"group_id": 308,
|
||||
"puzzle_difficulty": 2,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 67,
|
||||
"group_id": 307,
|
||||
"puzzle_difficulty": 0,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 68,
|
||||
"group_id": 307,
|
||||
"puzzle_difficulty": 1,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 69,
|
||||
"group_id": 307,
|
||||
"puzzle_difficulty": 2,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 64,
|
||||
"group_id": 306,
|
||||
"puzzle_difficulty": 0,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 65,
|
||||
"group_id": 306,
|
||||
"puzzle_difficulty": 1,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 66,
|
||||
"group_id": 306,
|
||||
"puzzle_difficulty": 2,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 61,
|
||||
"group_id": 305,
|
||||
"puzzle_difficulty": 0,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 62,
|
||||
"group_id": 305,
|
||||
"puzzle_difficulty": 1,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 63,
|
||||
"group_id": 305,
|
||||
"puzzle_difficulty": 2,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 46,
|
||||
"group_id": 304,
|
||||
"puzzle_difficulty": 0,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 47,
|
||||
"group_id": 304,
|
||||
"puzzle_difficulty": 1,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 48,
|
||||
"group_id": 304,
|
||||
"puzzle_difficulty": 2,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 43,
|
||||
"group_id": 303,
|
||||
"puzzle_difficulty": 0,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 44,
|
||||
"group_id": 303,
|
||||
"puzzle_difficulty": 1,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 45,
|
||||
"group_id": 303,
|
||||
"puzzle_difficulty": 2,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 40,
|
||||
"group_id": 302,
|
||||
"puzzle_difficulty": 0,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 41,
|
||||
"group_id": 302,
|
||||
"puzzle_difficulty": 1,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 42,
|
||||
"group_id": 302,
|
||||
"puzzle_difficulty": 2,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 37,
|
||||
"group_id": 301,
|
||||
"puzzle_difficulty": 0,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 38,
|
||||
"group_id": 301,
|
||||
"puzzle_difficulty": 1,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 39,
|
||||
"group_id": 301,
|
||||
"puzzle_difficulty": 2,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 87,
|
||||
"group_id": 9,
|
||||
"puzzle_difficulty": 0,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 88,
|
||||
"group_id": 9,
|
||||
"puzzle_difficulty": 1,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 89,
|
||||
"group_id": 9,
|
||||
"puzzle_difficulty": 2,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 90,
|
||||
"group_id": 9,
|
||||
"puzzle_difficulty": 3,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 80,
|
||||
"group_id": 8,
|
||||
"puzzle_difficulty": 0,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 81,
|
||||
"group_id": 8,
|
||||
"puzzle_difficulty": 1,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 82,
|
||||
"group_id": 8,
|
||||
"puzzle_difficulty": 2,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 83,
|
||||
"group_id": 8,
|
||||
"puzzle_difficulty": 3,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 70,
|
||||
"group_id": 7,
|
||||
"puzzle_difficulty": 0,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 71,
|
||||
"group_id": 7,
|
||||
"puzzle_difficulty": 1,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 72,
|
||||
"group_id": 7,
|
||||
"puzzle_difficulty": 1,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 73,
|
||||
"group_id": 7,
|
||||
"puzzle_difficulty": 2,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 52,
|
||||
"group_id": 6,
|
||||
"puzzle_difficulty": 0,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 53,
|
||||
"group_id": 6,
|
||||
"puzzle_difficulty": 0,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 54,
|
||||
"group_id": 6,
|
||||
"puzzle_difficulty": 1,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 55,
|
||||
"group_id": 6,
|
||||
"puzzle_difficulty": 1,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 56,
|
||||
"group_id": 6,
|
||||
"puzzle_difficulty": 1,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 57,
|
||||
"group_id": 6,
|
||||
"puzzle_difficulty": 2,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 58,
|
||||
"group_id": 6,
|
||||
"puzzle_difficulty": 2,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 59,
|
||||
"group_id": 6,
|
||||
"puzzle_difficulty": 3,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 60,
|
||||
"group_id": 6,
|
||||
"puzzle_difficulty": 3,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 49,
|
||||
"group_id": 5,
|
||||
"puzzle_difficulty": 0,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 50,
|
||||
"group_id": 5,
|
||||
"puzzle_difficulty": 1,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 51,
|
||||
"group_id": 5,
|
||||
"puzzle_difficulty": 2,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 28,
|
||||
"group_id": 4,
|
||||
"puzzle_difficulty": 0,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 29,
|
||||
"group_id": 4,
|
||||
"puzzle_difficulty": 0,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 30,
|
||||
"group_id": 4,
|
||||
"puzzle_difficulty": 1,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 31,
|
||||
"group_id": 4,
|
||||
"puzzle_difficulty": 1,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 32,
|
||||
"group_id": 4,
|
||||
"puzzle_difficulty": 1,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 33,
|
||||
"group_id": 4,
|
||||
"puzzle_difficulty": 2,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 34,
|
||||
"group_id": 4,
|
||||
"puzzle_difficulty": 2,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 35,
|
||||
"group_id": 4,
|
||||
"puzzle_difficulty": 2,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 36,
|
||||
"group_id": 4,
|
||||
"puzzle_difficulty": 3,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 20,
|
||||
"group_id": 3,
|
||||
"puzzle_difficulty": 0,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 21,
|
||||
"group_id": 3,
|
||||
"puzzle_difficulty": 0,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 22,
|
||||
"group_id": 3,
|
||||
"puzzle_difficulty": 1,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 23,
|
||||
"group_id": 3,
|
||||
"puzzle_difficulty": 1,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 24,
|
||||
"group_id": 3,
|
||||
"puzzle_difficulty": 1,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 25,
|
||||
"group_id": 3,
|
||||
"puzzle_difficulty": 2,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 26,
|
||||
"group_id": 3,
|
||||
"puzzle_difficulty": 2,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 27,
|
||||
"group_id": 3,
|
||||
"puzzle_difficulty": 2,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"group_id": 2,
|
||||
"puzzle_difficulty": 0,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"group_id": 2,
|
||||
"puzzle_difficulty": 0,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 12,
|
||||
"group_id": 2,
|
||||
"puzzle_difficulty": 1,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 13,
|
||||
"group_id": 2,
|
||||
"puzzle_difficulty": 1,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 14,
|
||||
"group_id": 2,
|
||||
"puzzle_difficulty": 1,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 15,
|
||||
"group_id": 2,
|
||||
"puzzle_difficulty": 2,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 16,
|
||||
"group_id": 2,
|
||||
"puzzle_difficulty": 2,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 17,
|
||||
"group_id": 2,
|
||||
"puzzle_difficulty": 3,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": "Puzzle_Unlock_Condition_0001"
|
||||
},
|
||||
{
|
||||
"id": 18,
|
||||
"group_id": 2,
|
||||
"puzzle_difficulty": 3,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": "Puzzle_Unlock_Condition_0001"
|
||||
},
|
||||
{
|
||||
"id": 19,
|
||||
"group_id": 2,
|
||||
"puzzle_difficulty": 3,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": "Puzzle_Unlock_Condition_0001"
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"group_id": 1,
|
||||
"puzzle_difficulty": 0,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"group_id": 1,
|
||||
"puzzle_difficulty": 0,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"group_id": 1,
|
||||
"puzzle_difficulty": 0,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"group_id": 1,
|
||||
"puzzle_difficulty": 1,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"group_id": 1,
|
||||
"puzzle_difficulty": 1,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"group_id": 1,
|
||||
"puzzle_difficulty": 1,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"group_id": 1,
|
||||
"puzzle_difficulty": 2,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"group_id": 1,
|
||||
"puzzle_difficulty": 2,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"group_id": 1,
|
||||
"puzzle_difficulty": 2,
|
||||
"is_additional": false,
|
||||
"is_playable": true,
|
||||
"release_condition_text_id": ""
|
||||
}
|
||||
]
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SVSim.Database;
|
||||
using SVSim.Database.Enums;
|
||||
@@ -31,8 +30,6 @@ public class GlobalsImporter
|
||||
JsonElement? mypageIndex = LoadCapture(capturesDir, "mypage-index");
|
||||
JsonElement? deckInfo = LoadCapture(capturesDir, "deck-info");
|
||||
JsonElement? packInfo = LoadCapture(capturesDir, "pack-info");
|
||||
JsonElement? basicPuzzleInfo = LoadCapture(capturesDir, "basic-puzzle-info");
|
||||
JsonElement? basicPuzzleMission = LoadCapture(capturesDir, "basic-puzzle-mission");
|
||||
|
||||
int total = 0;
|
||||
|
||||
@@ -73,17 +70,6 @@ public class GlobalsImporter
|
||||
total += await ImportPacks(context, packInfo.Value);
|
||||
}
|
||||
|
||||
if (basicPuzzleInfo.HasValue)
|
||||
{
|
||||
total += await ImportPuzzleGroups(context, basicPuzzleInfo.Value);
|
||||
total += await ImportPuzzles(context, basicPuzzleInfo.Value);
|
||||
}
|
||||
|
||||
if (basicPuzzleMission.HasValue)
|
||||
{
|
||||
total += await ImportPuzzleMissions(context, basicPuzzleMission.Value);
|
||||
}
|
||||
|
||||
await context.SaveChangesAsync();
|
||||
Console.WriteLine($"[GlobalsImporter] Done: {total} total rows changed.");
|
||||
return total;
|
||||
@@ -819,140 +805,6 @@ public class GlobalsImporter
|
||||
return created + updated;
|
||||
}
|
||||
|
||||
// ---------- Basic Puzzle Groups + Puzzles ----------
|
||||
|
||||
/// <summary>
|
||||
/// /basic_puzzle/info capture is an array of group objects keyed on puzzle_master_id.
|
||||
/// Numeric wire fields come through as strings — GetInt tolerates both. Idempotent upsert
|
||||
/// by puzzle_master_id; rows missing from a partial capture are left intact.
|
||||
/// </summary>
|
||||
private async Task<int> ImportPuzzleGroups(SVSimDbContext context, JsonElement infoData)
|
||||
{
|
||||
if (infoData.ValueKind != JsonValueKind.Array) return 0;
|
||||
|
||||
var existing = await context.PuzzleGroups.ToDictionaryAsync(e => e.Id);
|
||||
int created = 0, updated = 0;
|
||||
|
||||
foreach (var row in infoData.EnumerateArray())
|
||||
{
|
||||
int masterId = GetInt(row, "puzzle_master_id");
|
||||
if (masterId == 0) continue;
|
||||
|
||||
var entry = existing.TryGetValue(masterId, out var ex) ? ex : new PuzzleGroupEntry { Id = masterId };
|
||||
entry.BasicTitleTextId = GetString(row, "basic_title_text_id");
|
||||
entry.PuzzleCharaId = GetInt(row, "puzzle_chara_id");
|
||||
entry.CharaId = GetInt(row, "chara_id");
|
||||
entry.SortType = GetInt(row, "sort_type");
|
||||
entry.DifficultyNameListJson = row.TryGetProperty("puzzle_difficulty_name_list", out var d)
|
||||
? Serialize(d)
|
||||
: "{}";
|
||||
|
||||
if (ex is null) { context.PuzzleGroups.Add(entry); created++; }
|
||||
else updated++;
|
||||
}
|
||||
|
||||
Console.WriteLine($"[GlobalsImporter] PuzzleGroups: +{created}/~{updated}");
|
||||
return created + updated;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Walks each group's puzzle_data array and upserts PuzzleEntry rows keyed on puzzle_id.
|
||||
/// Groups must have been imported first (FK PuzzleEntry.GroupId → PuzzleGroupEntry.Id).
|
||||
/// </summary>
|
||||
private async Task<int> ImportPuzzles(SVSimDbContext context, JsonElement infoData)
|
||||
{
|
||||
if (infoData.ValueKind != JsonValueKind.Array) return 0;
|
||||
|
||||
var existing = await context.Puzzles.ToDictionaryAsync(e => e.Id);
|
||||
int created = 0, updated = 0;
|
||||
|
||||
foreach (var group in infoData.EnumerateArray())
|
||||
{
|
||||
int masterId = GetInt(group, "puzzle_master_id");
|
||||
if (masterId == 0 || !group.TryGetProperty("puzzle_data", out var puzzleArray)) continue;
|
||||
if (puzzleArray.ValueKind != JsonValueKind.Array) continue;
|
||||
|
||||
foreach (var p in puzzleArray.EnumerateArray())
|
||||
{
|
||||
int puzzleId = GetInt(p, "puzzle_id");
|
||||
if (puzzleId == 0) continue;
|
||||
|
||||
var entry = existing.TryGetValue(puzzleId, out var ex) ? ex : new PuzzleEntry { Id = puzzleId };
|
||||
entry.GroupId = masterId;
|
||||
entry.PuzzleDifficulty = GetInt(p, "puzzle_difficulty");
|
||||
entry.IsAdditional = GetBool(p, "is_additional");
|
||||
entry.IsPlayable = GetBool(p, "is_playable");
|
||||
entry.ReleaseConditionTextId = GetString(p, "release_condition_text_id");
|
||||
|
||||
if (ex is null) { context.Puzzles.Add(entry); created++; }
|
||||
else updated++;
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine($"[GlobalsImporter] Puzzles: +{created}/~{updated}");
|
||||
return created + updated;
|
||||
}
|
||||
|
||||
// ---------- Basic Puzzle Missions ----------
|
||||
|
||||
private static readonly Regex RoundMissionPattern =
|
||||
new(@"^Clear all Round (\d+) puzzles$", RegexOptions.Compiled);
|
||||
|
||||
/// <summary>Maps the captured mission_name to its target puzzle_master_id. Returns null for
|
||||
/// Special-Round entries — Phase 1 surfaces them with total_count=0 (see design § Out of Scope).</summary>
|
||||
internal static int? DeriveTargetPuzzleGroupId(string missionName)
|
||||
{
|
||||
var m = RoundMissionPattern.Match(missionName);
|
||||
return m.Success ? 300 + int.Parse(m.Groups[1].Value) : null;
|
||||
}
|
||||
|
||||
private async Task<int> ImportPuzzleMissions(SVSimDbContext context, JsonElement missionData)
|
||||
{
|
||||
if (missionData.ValueKind != JsonValueKind.Array) return 0;
|
||||
|
||||
// Key by 1-based sequence (the wire has no stable id); first run inserts, re-runs match by index.
|
||||
var existing = await context.PuzzleMissions.ToDictionaryAsync(e => e.Id);
|
||||
int created = 0, updated = 0, unmapped = 0;
|
||||
|
||||
int seq = 1;
|
||||
foreach (var row in missionData.EnumerateArray())
|
||||
{
|
||||
string name = GetString(row, "mission_name");
|
||||
if (string.IsNullOrEmpty(name)) { seq++; continue; }
|
||||
|
||||
var entry = existing.TryGetValue(seq, out var ex) ? ex : new PuzzleMissionEntry { Id = seq };
|
||||
entry.MissionName = name;
|
||||
entry.AchievedMessage = RoundMissionPattern.IsMatch(name)
|
||||
? RoundMissionPattern.Replace(name, m => $"Cleared all Round {m.Groups[1].Value} puzzles")
|
||||
: "Mission achieved"; // Special-Round fallback; only surfaces if a Special mission ever flips, which won't in Phase 1.
|
||||
entry.RequireNumber = GetInt(row, "require_number");
|
||||
entry.CampaignCommenceTime = GetLong(row, "campaign_commence_time");
|
||||
entry.OrderId = GetInt(row, "order_id");
|
||||
|
||||
// reward_list[0] — single reward per mission. Skip if missing/empty.
|
||||
if (row.TryGetProperty("reward_list", out var rl) && rl.ValueKind == JsonValueKind.Array && rl.GetArrayLength() > 0)
|
||||
{
|
||||
var r = rl[0];
|
||||
entry.RewardType = GetInt(r, "reward_type");
|
||||
entry.RewardDetailId = GetLong(r, "reward_detail_id");
|
||||
entry.RewardNumber = GetInt(r, "reward_number");
|
||||
}
|
||||
|
||||
entry.TargetPuzzleGroupId = DeriveTargetPuzzleGroupId(name);
|
||||
if (entry.TargetPuzzleGroupId is null) unmapped++;
|
||||
|
||||
if (ex is null) { context.PuzzleMissions.Add(entry); created++; }
|
||||
else updated++;
|
||||
|
||||
seq++;
|
||||
}
|
||||
|
||||
if (unmapped > 0)
|
||||
Console.WriteLine($"[GlobalsImporter] PuzzleMissions: {unmapped} Special-Round missions left unmapped (Phase 1 deferral).");
|
||||
Console.WriteLine($"[GlobalsImporter] PuzzleMissions: +{created}/~{updated}");
|
||||
return created + updated;
|
||||
}
|
||||
|
||||
// ---------- Helpers ----------
|
||||
|
||||
private static void WarnOrphans(string label, int count)
|
||||
|
||||
142
SVSim.Bootstrap/Importers/PuzzleImporter.cs
Normal file
142
SVSim.Bootstrap/Importers/PuzzleImporter.cs
Normal file
@@ -0,0 +1,142 @@
|
||||
using System.Text.Json;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SVSim.Bootstrap.Models.Seed;
|
||||
using SVSim.Database;
|
||||
using SVSim.Database.Models;
|
||||
|
||||
namespace SVSim.Bootstrap.Importers;
|
||||
|
||||
/// <summary>
|
||||
/// Idempotent upsert of the basic-puzzle catalog from <c>seeds/puzzle-groups.json</c>,
|
||||
/// <c>seeds/puzzles.json</c>, and <c>seeds/puzzle-missions.json</c>. Groups must be imported
|
||||
/// before puzzles (FK on <see cref="PuzzleEntry.GroupId"/> -> <see cref="PuzzleGroupEntry.Id"/>).
|
||||
/// Rows missing from the seed are LEFT INTACT (consistent with other per-importer seeds).
|
||||
/// </summary>
|
||||
public class PuzzleImporter
|
||||
{
|
||||
public async Task<int> ImportGroupsAsync(SVSimDbContext context, string seedDir)
|
||||
{
|
||||
string path = Path.Combine(seedDir, "puzzle-groups.json");
|
||||
var seed = SeedLoader.LoadList<PuzzleGroupSeed>(path);
|
||||
if (seed.Count == 0)
|
||||
{
|
||||
Console.WriteLine("[PuzzleImporter] No group seed rows; skipping.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
var existing = await context.PuzzleGroups.ToDictionaryAsync(e => e.Id);
|
||||
int created = 0, updated = 0;
|
||||
|
||||
foreach (var s in seed)
|
||||
{
|
||||
if (s.Id == 0) continue;
|
||||
|
||||
var entry = existing.TryGetValue(s.Id, out var ex)
|
||||
? ex : new PuzzleGroupEntry { Id = s.Id };
|
||||
|
||||
entry.BasicTitleTextId = s.BasicTitleTextId;
|
||||
entry.PuzzleCharaId = s.PuzzleCharaId;
|
||||
entry.CharaId = s.CharaId;
|
||||
entry.SortType = s.SortType;
|
||||
entry.DifficultyNameListJson = s.DifficultyNameList.ValueKind == JsonValueKind.Undefined
|
||||
? "{}"
|
||||
: JsonSerializer.Serialize(s.DifficultyNameList);
|
||||
|
||||
if (ex is null)
|
||||
{
|
||||
context.PuzzleGroups.Add(entry);
|
||||
existing[s.Id] = entry;
|
||||
created++;
|
||||
}
|
||||
else updated++;
|
||||
}
|
||||
|
||||
await context.SaveChangesAsync();
|
||||
Console.WriteLine($"[PuzzleImporter] Groups +{created}/~{updated}");
|
||||
return created + updated;
|
||||
}
|
||||
|
||||
public async Task<int> ImportPuzzlesAsync(SVSimDbContext context, string seedDir)
|
||||
{
|
||||
string path = Path.Combine(seedDir, "puzzles.json");
|
||||
var seed = SeedLoader.LoadList<PuzzleSeed>(path);
|
||||
if (seed.Count == 0)
|
||||
{
|
||||
Console.WriteLine("[PuzzleImporter] No puzzle seed rows; skipping.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
var existing = await context.Puzzles.ToDictionaryAsync(e => e.Id);
|
||||
int created = 0, updated = 0;
|
||||
|
||||
foreach (var s in seed)
|
||||
{
|
||||
if (s.Id == 0) continue;
|
||||
|
||||
var entry = existing.TryGetValue(s.Id, out var ex)
|
||||
? ex : new PuzzleEntry { Id = s.Id };
|
||||
|
||||
entry.GroupId = s.GroupId;
|
||||
entry.PuzzleDifficulty = s.PuzzleDifficulty;
|
||||
entry.IsAdditional = s.IsAdditional;
|
||||
entry.IsPlayable = s.IsPlayable;
|
||||
entry.ReleaseConditionTextId = s.ReleaseConditionTextId;
|
||||
|
||||
if (ex is null)
|
||||
{
|
||||
context.Puzzles.Add(entry);
|
||||
existing[s.Id] = entry;
|
||||
created++;
|
||||
}
|
||||
else updated++;
|
||||
}
|
||||
|
||||
await context.SaveChangesAsync();
|
||||
Console.WriteLine($"[PuzzleImporter] Puzzles +{created}/~{updated}");
|
||||
return created + updated;
|
||||
}
|
||||
|
||||
public async Task<int> ImportMissionsAsync(SVSimDbContext context, string seedDir)
|
||||
{
|
||||
string path = Path.Combine(seedDir, "puzzle-missions.json");
|
||||
var seed = SeedLoader.LoadList<PuzzleMissionSeed>(path);
|
||||
if (seed.Count == 0)
|
||||
{
|
||||
Console.WriteLine("[PuzzleImporter] No mission seed rows; skipping.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
var existing = await context.PuzzleMissions.ToDictionaryAsync(e => e.Id);
|
||||
int created = 0, updated = 0;
|
||||
|
||||
foreach (var s in seed)
|
||||
{
|
||||
if (s.Id == 0) continue;
|
||||
|
||||
var entry = existing.TryGetValue(s.Id, out var ex)
|
||||
? ex : new PuzzleMissionEntry { Id = s.Id };
|
||||
|
||||
entry.MissionName = s.MissionName;
|
||||
entry.AchievedMessage = s.AchievedMessage;
|
||||
entry.RequireNumber = s.RequireNumber;
|
||||
entry.CampaignCommenceTime = s.CampaignCommenceTime;
|
||||
entry.OrderId = s.OrderId;
|
||||
entry.RewardType = s.RewardType;
|
||||
entry.RewardDetailId = s.RewardDetailId;
|
||||
entry.RewardNumber = s.RewardNumber;
|
||||
entry.TargetPuzzleGroupId = s.TargetPuzzleGroupId;
|
||||
|
||||
if (ex is null)
|
||||
{
|
||||
context.PuzzleMissions.Add(entry);
|
||||
existing[s.Id] = entry;
|
||||
created++;
|
||||
}
|
||||
else updated++;
|
||||
}
|
||||
|
||||
await context.SaveChangesAsync();
|
||||
Console.WriteLine($"[PuzzleImporter] Missions +{created}/~{updated}");
|
||||
return created + updated;
|
||||
}
|
||||
}
|
||||
14
SVSim.Bootstrap/Models/Seed/PuzzleGroupSeed.cs
Normal file
14
SVSim.Bootstrap/Models/Seed/PuzzleGroupSeed.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace SVSim.Bootstrap.Models.Seed;
|
||||
|
||||
public sealed class PuzzleGroupSeed
|
||||
{
|
||||
[JsonPropertyName("id")] public int Id { get; set; }
|
||||
[JsonPropertyName("basic_title_text_id")] public string BasicTitleTextId { get; set; } = "";
|
||||
[JsonPropertyName("puzzle_chara_id")] public int PuzzleCharaId { get; set; }
|
||||
[JsonPropertyName("chara_id")] public int CharaId { get; set; }
|
||||
[JsonPropertyName("sort_type")] public int SortType { get; set; }
|
||||
[JsonPropertyName("difficulty_name_list")] public JsonElement DifficultyNameList { get; set; }
|
||||
}
|
||||
17
SVSim.Bootstrap/Models/Seed/PuzzleMissionSeed.cs
Normal file
17
SVSim.Bootstrap/Models/Seed/PuzzleMissionSeed.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace SVSim.Bootstrap.Models.Seed;
|
||||
|
||||
public sealed class PuzzleMissionSeed
|
||||
{
|
||||
[JsonPropertyName("id")] public int Id { get; set; }
|
||||
[JsonPropertyName("mission_name")] public string MissionName { get; set; } = "";
|
||||
[JsonPropertyName("achieved_message")] public string AchievedMessage { get; set; } = "";
|
||||
[JsonPropertyName("require_number")] public int RequireNumber { get; set; }
|
||||
[JsonPropertyName("campaign_commence_time")] public long CampaignCommenceTime { get; set; }
|
||||
[JsonPropertyName("order_id")] public int OrderId { get; set; }
|
||||
[JsonPropertyName("reward_type")] public int RewardType { get; set; }
|
||||
[JsonPropertyName("reward_detail_id")] public long RewardDetailId { get; set; }
|
||||
[JsonPropertyName("reward_number")] public int RewardNumber { get; set; }
|
||||
[JsonPropertyName("target_puzzle_group_id")] public int? TargetPuzzleGroupId { get; set; }
|
||||
}
|
||||
13
SVSim.Bootstrap/Models/Seed/PuzzleSeed.cs
Normal file
13
SVSim.Bootstrap/Models/Seed/PuzzleSeed.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace SVSim.Bootstrap.Models.Seed;
|
||||
|
||||
public sealed class PuzzleSeed
|
||||
{
|
||||
[JsonPropertyName("id")] public int Id { get; set; }
|
||||
[JsonPropertyName("group_id")] public int GroupId { get; set; }
|
||||
[JsonPropertyName("puzzle_difficulty")] public int PuzzleDifficulty { get; set; }
|
||||
[JsonPropertyName("is_additional")] public bool IsAdditional { get; set; }
|
||||
[JsonPropertyName("is_playable")] public bool IsPlayable { get; set; }
|
||||
[JsonPropertyName("release_condition_text_id")] public string ReleaseConditionTextId { get; set; } = "";
|
||||
}
|
||||
@@ -78,6 +78,10 @@ public static class Program
|
||||
await new GlobalsImporter().ImportAllAsync(context, opts.CapturesDir);
|
||||
await new PracticeOpponentImporter().ImportAsync(context, opts.SeedDir);
|
||||
await new PaymentItemImporter().ImportAsync(context, opts.SeedDir);
|
||||
var puzzleImporter = new PuzzleImporter();
|
||||
await puzzleImporter.ImportGroupsAsync(context, opts.SeedDir);
|
||||
await puzzleImporter.ImportPuzzlesAsync(context, opts.SeedDir);
|
||||
await puzzleImporter.ImportMissionsAsync(context, opts.SeedDir);
|
||||
|
||||
// BuildDeck pipeline: series CSV → catalog JSON → package CSV. Catalog must run after
|
||||
// series CSV (FK on products → series) and before package CSV (so the catalog-side
|
||||
|
||||
154
SVSim.UnitTests/Importers/PuzzleImporterTests.cs
Normal file
154
SVSim.UnitTests/Importers/PuzzleImporterTests.cs
Normal file
@@ -0,0 +1,154 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using SVSim.Bootstrap.Importers;
|
||||
using SVSim.Database;
|
||||
using SVSim.Database.Models;
|
||||
using SVSim.UnitTests.Infrastructure;
|
||||
|
||||
namespace SVSim.UnitTests.Importers;
|
||||
|
||||
public class PuzzleImporterTests
|
||||
{
|
||||
private static string SeedDir => Path.Combine(AppContext.BaseDirectory, "Data", "seeds");
|
||||
|
||||
[Test]
|
||||
public async Task ImportsGroups_PuzzlesAndMissions_from_seed_files()
|
||||
{
|
||||
using var factory = new SVSimTestFactory();
|
||||
using var scope = factory.Services.CreateScope();
|
||||
var db = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
|
||||
|
||||
var importer = new PuzzleImporter();
|
||||
await importer.ImportGroupsAsync(db, SeedDir);
|
||||
await importer.ImportPuzzlesAsync(db, SeedDir);
|
||||
await importer.ImportMissionsAsync(db, SeedDir);
|
||||
|
||||
int groupCount = await db.PuzzleGroups.CountAsync();
|
||||
int puzzleCount = await db.Puzzles.CountAsync();
|
||||
int missionCount = await db.PuzzleMissions.CountAsync();
|
||||
|
||||
Assert.That(groupCount, Is.GreaterThan(0), "seed must contain groups");
|
||||
Assert.That(puzzleCount, Is.GreaterThan(0), "seed must contain puzzles");
|
||||
Assert.That(missionCount, Is.GreaterThan(0), "seed must contain missions");
|
||||
|
||||
// Every puzzle's GroupId must reference an existing group (FK satisfied).
|
||||
var groupIds = await db.PuzzleGroups.Select(g => g.Id).ToListAsync();
|
||||
var groupIdSet = new HashSet<int>(groupIds);
|
||||
var puzzleGroupIds = await db.Puzzles.Select(p => p.GroupId).Distinct().ToListAsync();
|
||||
foreach (var gid in puzzleGroupIds)
|
||||
{
|
||||
Assert.That(groupIdSet, Does.Contain(gid),
|
||||
$"puzzle references unknown group_id={gid}");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Is_idempotent_on_rerun()
|
||||
{
|
||||
using var factory = new SVSimTestFactory();
|
||||
using var scope = factory.Services.CreateScope();
|
||||
var db = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
|
||||
|
||||
var importer = new PuzzleImporter();
|
||||
await importer.ImportGroupsAsync(db, SeedDir);
|
||||
await importer.ImportPuzzlesAsync(db, SeedDir);
|
||||
await importer.ImportMissionsAsync(db, SeedDir);
|
||||
|
||||
int g1 = await db.PuzzleGroups.CountAsync();
|
||||
int p1 = await db.Puzzles.CountAsync();
|
||||
int m1 = await db.PuzzleMissions.CountAsync();
|
||||
|
||||
await importer.ImportGroupsAsync(db, SeedDir);
|
||||
await importer.ImportPuzzlesAsync(db, SeedDir);
|
||||
await importer.ImportMissionsAsync(db, SeedDir);
|
||||
|
||||
Assert.That(await db.PuzzleGroups.CountAsync(), Is.EqualTo(g1));
|
||||
Assert.That(await db.Puzzles.CountAsync(), Is.EqualTo(p1));
|
||||
Assert.That(await db.PuzzleMissions.CountAsync(), Is.EqualTo(m1));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Leaves_existing_rows_untouched_when_missing_from_seed()
|
||||
{
|
||||
using var factory = new SVSimTestFactory();
|
||||
using var scope = factory.Services.CreateScope();
|
||||
var db = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
|
||||
|
||||
const int legacyGroupId = 99999;
|
||||
const int legacyPuzzleId = 99998;
|
||||
const int legacyMissionId = 99997;
|
||||
|
||||
db.PuzzleGroups.Add(new PuzzleGroupEntry
|
||||
{
|
||||
Id = legacyGroupId,
|
||||
BasicTitleTextId = "legacy_group",
|
||||
DifficultyNameListJson = "{\"legacy\":\"1\"}",
|
||||
});
|
||||
db.Puzzles.Add(new PuzzleEntry
|
||||
{
|
||||
Id = legacyPuzzleId,
|
||||
GroupId = legacyGroupId,
|
||||
PuzzleDifficulty = 5,
|
||||
ReleaseConditionTextId = "legacy_puzzle",
|
||||
});
|
||||
db.PuzzleMissions.Add(new PuzzleMissionEntry
|
||||
{
|
||||
Id = legacyMissionId,
|
||||
MissionName = "legacy_mission",
|
||||
AchievedMessage = "legacy_achieved",
|
||||
RequireNumber = 42,
|
||||
});
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
var importer = new PuzzleImporter();
|
||||
await importer.ImportGroupsAsync(db, SeedDir);
|
||||
await importer.ImportPuzzlesAsync(db, SeedDir);
|
||||
await importer.ImportMissionsAsync(db, SeedDir);
|
||||
|
||||
var g = await db.PuzzleGroups.FindAsync(legacyGroupId);
|
||||
Assert.That(g, Is.Not.Null);
|
||||
Assert.That(g!.BasicTitleTextId, Is.EqualTo("legacy_group"));
|
||||
|
||||
var p = await db.Puzzles.FindAsync(legacyPuzzleId);
|
||||
Assert.That(p, Is.Not.Null);
|
||||
Assert.That(p!.PuzzleDifficulty, Is.EqualTo(5));
|
||||
|
||||
var m = await db.PuzzleMissions.FindAsync(legacyMissionId);
|
||||
Assert.That(m, Is.Not.Null);
|
||||
Assert.That(m!.MissionName, Is.EqualTo("legacy_mission"));
|
||||
Assert.That(m.RequireNumber, Is.EqualTo(42));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Skips_rows_with_zero_id()
|
||||
{
|
||||
using var factory = new SVSimTestFactory();
|
||||
using var scope = factory.Services.CreateScope();
|
||||
var db = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
|
||||
|
||||
string tmp = Path.Combine(Path.GetTempPath(), $"seed-{Guid.NewGuid()}");
|
||||
Directory.CreateDirectory(tmp);
|
||||
try
|
||||
{
|
||||
File.WriteAllText(Path.Combine(tmp, "puzzle-groups.json"),
|
||||
"[{\"id\":0,\"basic_title_text_id\":\"junk\"}]");
|
||||
File.WriteAllText(Path.Combine(tmp, "puzzles.json"),
|
||||
"[{\"id\":0,\"group_id\":1,\"puzzle_difficulty\":1}]");
|
||||
File.WriteAllText(Path.Combine(tmp, "puzzle-missions.json"),
|
||||
"[{\"id\":0,\"mission_name\":\"junk\"}]");
|
||||
|
||||
var importer = new PuzzleImporter();
|
||||
await importer.ImportGroupsAsync(db, tmp);
|
||||
await importer.ImportPuzzlesAsync(db, tmp);
|
||||
await importer.ImportMissionsAsync(db, tmp);
|
||||
|
||||
Assert.That(await db.PuzzleGroups.CountAsync(), Is.EqualTo(0),
|
||||
"rows with id=0 must not be inserted into groups");
|
||||
Assert.That(await db.Puzzles.CountAsync(), Is.EqualTo(0),
|
||||
"rows with id=0 must not be inserted into puzzles");
|
||||
Assert.That(await db.PuzzleMissions.CountAsync(), Is.EqualTo(0),
|
||||
"rows with id=0 must not be inserted into missions");
|
||||
}
|
||||
finally { Directory.Delete(tmp, true); }
|
||||
}
|
||||
}
|
||||
@@ -194,6 +194,10 @@ internal sealed class SVSimTestFactory : WebApplicationFactory<Program>
|
||||
// practice-opponent rows after the corresponding block was lifted out of GlobalsImporter.
|
||||
await new PracticeOpponentImporter().ImportAsync(ctx, seedDir);
|
||||
await new PaymentItemImporter().ImportAsync(ctx, seedDir);
|
||||
var puzzleImporter = new PuzzleImporter();
|
||||
await puzzleImporter.ImportGroupsAsync(ctx, seedDir);
|
||||
await puzzleImporter.ImportPuzzlesAsync(ctx, seedDir);
|
||||
await puzzleImporter.ImportMissionsAsync(ctx, seedDir);
|
||||
}
|
||||
|
||||
/// <summary>Convenience: bake the X-Test-Viewer-Id header into a fresh client.</summary>
|
||||
|
||||
Reference in New Issue
Block a user