From aa8064350586ab3d564e651be76643c87f936962 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 1 Jul 2025 08:59:27 +0000 Subject: [PATCH 1/3] Initial plan From 8283b49f11e605740d38928e4ea993d626de2dcd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 1 Jul 2025 09:08:38 +0000 Subject: [PATCH 2/3] =?UTF-8?q?=E6=A9=9F=E8=83=BD=E5=AE=9F=E8=A3=85?= =?UTF-8?q?=E5=AE=8C=E4=BA=86:=20=E3=83=A2=E3=83=B3=E3=82=B9=E3=82=BF?= =?UTF-8?q?=E3=83=BC=E6=A4=9C=E7=B4=A2=E3=81=AB=E4=BD=BF=E7=94=A8=E5=B1=9E?= =?UTF-8?q?=E6=80=A7=E3=81=A8=E5=BC=B1=E7=82=B9=E5=B1=9E=E6=80=A7=E3=83=95?= =?UTF-8?q?=E3=82=A3=E3=83=AB=E3=82=BF=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: o-ga09 <54522966+o-ga09@users.noreply.github.com> --- cmd/mcp/main.go | 22 +++++++-- internal/controller/monster/request.go | 12 +++-- .../database/mysql/monsterQueryService.go | 27 +++++++++++ .../mysql/monsterQueryService_test.go | 47 ++++++++++++++++--- 4 files changed, 92 insertions(+), 16 deletions(-) diff --git a/cmd/mcp/main.go b/cmd/mcp/main.go index 1ebdf621..bb2841b8 100644 --- a/cmd/mcp/main.go +++ b/cmd/mcp/main.go @@ -93,6 +93,14 @@ func (m *MCPServer) AddTools() []server.ServerTool { "type": "string", "description": "Filter by monster name (optional, supports partial matches)", }, + "usage_element": map[string]interface{}{ + "type": "string", + "description": "Filter by monster's usage element (optional, e.g., 'Fire', 'Water', 'Lightning', 'Ice', 'Dragon')", + }, + "weakness_element": map[string]interface{}{ + "type": "string", + "description": "Filter by monster's weakness element (optional, e.g., 'Fire', 'Water', 'Lightning', 'Ice', 'Dragon')", + }, "sort": map[string]interface{}{ "type": "string", "description": "Sort order for the results (optional, 'asc' for ascending, 'desc' for descending, default is 'asc')", @@ -252,14 +260,18 @@ func (m *MCPServer) getMonsters(ctx context.Context, args mcp.CallToolRequest) ( } monsterIDs := args.GetString("monster_ids", "") name := args.GetString("name", "") + usageElement := args.GetString("usage_element", "") + weaknessElement := args.GetString("weakness_element", "") sort := args.GetString("sort", "asc") param := request.RequestParam{ - MonsterIds: monsterIDs, - MonsterName: name, - Sort: sort, - Limit: limit, - Offset: (offset - 1) * limit, + MonsterIds: monsterIDs, + MonsterName: name, + UsageElement: usageElement, + WeaknessElement: weaknessElement, + Sort: sort, + Limit: limit, + Offset: (offset - 1) * limit, } ctx = context.WithValue(ctx, "param", param) diff --git a/internal/controller/monster/request.go b/internal/controller/monster/request.go index 1b3c38fd..d6182e1e 100644 --- a/internal/controller/monster/request.go +++ b/internal/controller/monster/request.go @@ -20,11 +20,13 @@ type Json struct { } type RequestParam struct { - MonsterIds string `form:"MonsterIds" json:"MonsterIds,omitempty"` - MonsterName string `form:"MonsterName" json:"MonsterName,omitempty"` - Limit int `form:"limit" json:"limit,omitempty" validate:"omitempty,min=0"` - Offset int `form:"offset" json:"offset,omitempty" validate:"omitempty,min=0"` - Sort string `form:"sort" json:"sort,omitempty" validate:"omitempty,oneof=asc desc"` + MonsterIds string `form:"MonsterIds" json:"MonsterIds,omitempty"` + MonsterName string `form:"MonsterName" json:"MonsterName,omitempty"` + UsageElement string `form:"UsageElement" json:"UsageElement,omitempty"` + WeaknessElement string `form:"WeaknessElement" json:"WeaknessElement,omitempty"` + Limit int `form:"limit" json:"limit,omitempty" validate:"omitempty,min=0"` + Offset int `form:"offset" json:"offset,omitempty" validate:"omitempty,min=0"` + Sort string `form:"sort" json:"sort,omitempty" validate:"omitempty,oneof=asc desc"` } type RequestRankingParam struct { diff --git a/internal/database/mysql/monsterQueryService.go b/internal/database/mysql/monsterQueryService.go index cf52b1cd..c46692e7 100644 --- a/internal/database/mysql/monsterQueryService.go +++ b/internal/database/mysql/monsterQueryService.go @@ -124,6 +124,33 @@ func (s *monsterQueryService) FetchList(ctx context.Context, id string) ([]*mons where_clade += " name LIKE '%" + p.MonsterName + "%' " } + // Add usage element search + if p.UsageElement != "" { + if where_clade != "" { + where_clade += " and element = '" + p.UsageElement + "' " + } else { + where_clade += " element = '" + p.UsageElement + "' " + } + } + + // Add weakness element search - need to join with Weakness table + if p.WeaknessElement != "" { + weaknessJoin := " EXISTS (SELECT 1 FROM weakness w WHERE w.monster_id = monster.monster_id AND " + + "(w.first_weak_element = '" + p.WeaknessElement + "' OR " + + "w.second_weak_element = '" + p.WeaknessElement + "' OR " + + "w.fire = '" + p.WeaknessElement + "' OR " + + "w.water = '" + p.WeaknessElement + "' OR " + + "w.lightning = '" + p.WeaknessElement + "' OR " + + "w.ice = '" + p.WeaknessElement + "' OR " + + "w.dragon = '" + p.WeaknessElement + "'))" + + if where_clade != "" { + where_clade += " and " + weaknessJoin + } else { + where_clade += weaknessJoin + } + } + if p.Sort == "1" { sort = "CAST(monster_id AS UNSIGNED) DESC" } else { diff --git a/internal/database/mysql/monsterQueryService_test.go b/internal/database/mysql/monsterQueryService_test.go index ce8faba8..16c3f36c 100644 --- a/internal/database/mysql/monsterQueryService_test.go +++ b/internal/database/mysql/monsterQueryService_test.go @@ -44,9 +44,12 @@ func Test_monsterQueryService_FetchList(t *testing.T) { bgm3 := music.NewMusic("0000000003", "0000000003", "ティガレックスのテーマ", "https://www.youtube.com/watch?v=3") // ID順に並んでいるデータを適応 - monster1 := &monsters.FetchMonsterListDto{Id: "0000000001", Name: "リオレウス", Description: "空の王者。", Location: []string{"古代樹の森"}, Category: "飛竜種", Title: []string{"MH"}, FirstWeak_Attack: "頭部", SecondWeak_Attack: "翼", FirstWeak_Element: "龍", SecondWeak_Element: "雷", Weakness_attack: weak_A, Weakness_element: weak_E, Ranking: ranking1, BGM: []music.Music{*bgm1}} - monster2 := &monsters.FetchMonsterListDto{Id: "0000000002", Name: "リオレイア", Description: "陸の女王", Location: []string{"古代樹の森"}, Category: "飛竜種", Title: []string{"MH"}, FirstWeak_Attack: "頭部", SecondWeak_Attack: "翼", FirstWeak_Element: "龍", SecondWeak_Element: "雷", Weakness_attack: weak_A, Weakness_element: weak_E, Ranking: ranking2, BGM: []music.Music{*bgm2}} - monster3 := &monsters.FetchMonsterListDto{Id: "0000000003", Name: "ティガレックス", Description: "絶対強者", Location: []string{"古代樹の森"}, Category: "飛竜種", Title: []string{"MH"}, FirstWeak_Attack: "頭部", SecondWeak_Attack: "翼", FirstWeak_Element: "雷", SecondWeak_Element: "水", Weakness_attack: weak_A, Weakness_element: weak_E, Ranking: ranking3, BGM: []music.Music{*bgm3}} + fireElement := "Fire" + waterElement := "Water" + dragonElement := "Dragon" + monster1 := &monsters.FetchMonsterListDto{Id: "0000000001", Name: "リオレウス", Description: "空の王者。", Element: &fireElement, Location: []string{"古代樹の森"}, Category: "飛竜種", Title: []string{"MH"}, FirstWeak_Attack: "頭部", SecondWeak_Attack: "翼", FirstWeak_Element: "龍", SecondWeak_Element: "雷", Weakness_attack: weak_A, Weakness_element: weak_E, Ranking: ranking1, BGM: []music.Music{*bgm1}} + monster2 := &monsters.FetchMonsterListDto{Id: "0000000002", Name: "リオレイア", Description: "陸の女王", Element: &waterElement, Location: []string{"古代樹の森"}, Category: "飛竜種", Title: []string{"MH"}, FirstWeak_Attack: "頭部", SecondWeak_Attack: "翼", FirstWeak_Element: "龍", SecondWeak_Element: "雷", Weakness_attack: weak_A, Weakness_element: weak_E, Ranking: ranking2, BGM: []music.Music{*bgm2}} + monster3 := &monsters.FetchMonsterListDto{Id: "0000000003", Name: "ティガレックス", Description: "絶対強者", Element: &dragonElement, Location: []string{"古代樹の森"}, Category: "飛竜種", Title: []string{"MH"}, FirstWeak_Attack: "頭部", SecondWeak_Attack: "翼", FirstWeak_Element: "雷", SecondWeak_Element: "水", Weakness_attack: weak_A, Weakness_element: weak_E, Ranking: ranking3, BGM: []music.Music{*bgm3}} param1 := param.RequestParam{MonsterIds: "", MonsterName: "", Limit: 100, Offset: 0, Sort: "1"} param2 := param.RequestParam{MonsterIds: "0000000001,0000000002", MonsterName: "", Limit: 100, Offset: 0, Sort: "1"} @@ -54,6 +57,10 @@ func Test_monsterQueryService_FetchList(t *testing.T) { param4 := param.RequestParam{MonsterIds: "", MonsterName: "", Limit: 100, Offset: 0, Sort: "1"} param5 := param.RequestParam{MonsterIds: "", MonsterName: "", Limit: 100, Offset: 0, Sort: "2"} param6 := param.RequestParam{MonsterIds: "0000000001", MonsterName: "リオレウス", Limit: 100, Offset: 0, Sort: "1"} + param7 := param.RequestParam{MonsterIds: "", MonsterName: "", UsageElement: "Fire", Limit: 100, Offset: 0, Sort: "1"} + param8 := param.RequestParam{MonsterIds: "", MonsterName: "", WeaknessElement: "龍", Limit: 100, Offset: 0, Sort: "1"} + param9 := param.RequestParam{MonsterIds: "", MonsterName: "", WeaknessElement: "雷", Limit: 100, Offset: 0, Sort: "1"} + param10 := param.RequestParam{MonsterIds: "", MonsterName: "", UsageElement: "Water", WeaknessElement: "龍", Limit: 100, Offset: 0, Sort: "1"} type args struct { id string @@ -112,6 +119,30 @@ func Test_monsterQueryService_FetchList(t *testing.T) { want: []*monsters.FetchMonsterListDto{monster1}, wantErr: false, }, + { + name: "DBからモンスターデータを使用属性(Fire)で検索して取得できる", + args: args{id: "", param: param7}, + want: []*monsters.FetchMonsterListDto{monster1}, + wantErr: false, + }, + { + name: "DBからモンスターデータを弱点属性(龍)で検索して取得できる", + args: args{id: "", param: param8}, + want: []*monsters.FetchMonsterListDto{monster2, monster1}, + wantErr: false, + }, + { + name: "DBからモンスターデータを弱点属性(雷)で検索して取得できる", + args: args{id: "", param: param9}, + want: []*monsters.FetchMonsterListDto{monster3, monster2, monster1}, + wantErr: false, + }, + { + name: "DBからモンスターデータを使用属性(Water)と弱点属性(龍)の組み合わせで検索して取得できる", + args: args{id: "", param: param10}, + want: []*monsters.FetchMonsterListDto{monster2}, + wantErr: false, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -139,10 +170,14 @@ func createMonsterData(t *testing.T, ctx context.Context) []*monsters.FetchMonst weak3 := []*Weakness{ {MonsterId: "0000000003", PartId: "0001", Fire: "45", Water: "45", Lightning: "45", Ice: "45", Dragon: "45", Slashing: "45", Blow: "45", Bullet: "45", FirstWeakAttack: "頭部", SecondWeakAttack: "翼", FirstWeakElement: "雷", SecondWeakElement: "水"}, } + fireElement := "Fire" + waterElement := "Water" + dragonElement := "Dragon" + monster := []Monster{ - {MonsterId: "0000000001", Name: "リオレウス", Description: "空の王者。", Field: []*Field{{FieldId: "0001", MonsterId: "0000000001", Name: "古代樹の森", ImageUrl: "images/kodaizyu.png"}}, Tribe: &Tribe{TribeId: "0001", MonsterId: "0000000001", Name_ja: "飛竜種", Name_en: "wibarn", Description: "飛竜種"}, Product: []*Product{{ProductId: "0001", MonsterId: "0000000001", Name: "MH", PublishYear: "2004", TotalSales: "200万本"}}, Weakness: weak1, Ranking: []*Ranking{{MonsterId: "0000000001", Ranking: "1", VoteYear: "2024/3/12"}}}, - {MonsterId: "0000000002", Name: "リオレイア", Description: "陸の女王", Field: []*Field{{FieldId: "0001", MonsterId: "0000000002", Name: "古代樹の森", ImageUrl: "images/kodaizyu.png"}}, Tribe: &Tribe{TribeId: "0001", MonsterId: "0000000002", Name_ja: "飛竜種", Name_en: "wibarn", Description: "飛竜種"}, Product: []*Product{{ProductId: "0001", MonsterId: "0000000002", Name: "MH", PublishYear: "2004", TotalSales: "200万本"}}, Weakness: weak2, Ranking: []*Ranking{{MonsterId: "0000000002", Ranking: "2", VoteYear: "2024/3/12"}}}, - {MonsterId: "0000000003", Name: "ティガレックス", Description: "絶対強者", Field: []*Field{{FieldId: "0001", MonsterId: "0000000003", Name: "古代樹の森", ImageUrl: "images/kodaizyu.png"}}, Tribe: &Tribe{TribeId: "0001", MonsterId: "0000000003", Name_ja: "飛竜種", Name_en: "wibarn", Description: "飛竜種"}, Product: []*Product{{ProductId: "0001", MonsterId: "0000000003", Name: "MH", PublishYear: "2004", TotalSales: "200万本"}}, Weakness: weak3, Ranking: []*Ranking{{MonsterId: "0000000003", Ranking: "3", VoteYear: "2024/3/12"}}}, + {MonsterId: "0000000001", Name: "リオレウス", Description: "空の王者。", Element: &fireElement, Field: []*Field{{FieldId: "0001", MonsterId: "0000000001", Name: "古代樹の森", ImageUrl: "images/kodaizyu.png"}}, Tribe: &Tribe{TribeId: "0001", MonsterId: "0000000001", Name_ja: "飛竜種", Name_en: "wibarn", Description: "飛竜種"}, Product: []*Product{{ProductId: "0001", MonsterId: "0000000001", Name: "MH", PublishYear: "2004", TotalSales: "200万本"}}, Weakness: weak1, Ranking: []*Ranking{{MonsterId: "0000000001", Ranking: "1", VoteYear: "2024/3/12"}}}, + {MonsterId: "0000000002", Name: "リオレイア", Description: "陸の女王", Element: &waterElement, Field: []*Field{{FieldId: "0001", MonsterId: "0000000002", Name: "古代樹の森", ImageUrl: "images/kodaizyu.png"}}, Tribe: &Tribe{TribeId: "0001", MonsterId: "0000000002", Name_ja: "飛竜種", Name_en: "wibarn", Description: "飛竜種"}, Product: []*Product{{ProductId: "0001", MonsterId: "0000000002", Name: "MH", PublishYear: "2004", TotalSales: "200万本"}}, Weakness: weak2, Ranking: []*Ranking{{MonsterId: "0000000002", Ranking: "2", VoteYear: "2024/3/12"}}}, + {MonsterId: "0000000003", Name: "ティガレックス", Description: "絶対強者", Element: &dragonElement, Field: []*Field{{FieldId: "0001", MonsterId: "0000000003", Name: "古代樹の森", ImageUrl: "images/kodaizyu.png"}}, Tribe: &Tribe{TribeId: "0001", MonsterId: "0000000003", Name_ja: "飛竜種", Name_en: "wibarn", Description: "飛竜種"}, Product: []*Product{{ProductId: "0001", MonsterId: "0000000003", Name: "MH", PublishYear: "2004", TotalSales: "200万本"}}, Weakness: weak3, Ranking: []*Ranking{{MonsterId: "0000000003", Ranking: "3", VoteYear: "2024/3/12"}}}, } bgms := []Music{ From 608f01732e1da746d043e42b477346708dde2628 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 1 Jul 2025 09:11:14 +0000 Subject: [PATCH 3/3] =?UTF-8?q?=E3=82=BB=E3=82=AD=E3=83=A5=E3=83=AA?= =?UTF-8?q?=E3=83=86=E3=82=A3=E6=94=B9=E5=96=84:=20=E3=83=91=E3=83=A9?= =?UTF-8?q?=E3=83=A1=E3=83=BC=E3=82=BF=E5=8C=96=E3=82=AF=E3=82=A8=E3=83=AA?= =?UTF-8?q?=E3=81=A7SQL=E3=82=A4=E3=83=B3=E3=82=B8=E3=82=A7=E3=82=AF?= =?UTF-8?q?=E3=82=B7=E3=83=A7=E3=83=B3=E5=AF=BE=E7=AD=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: o-ga09 <54522966+o-ga09@users.noreply.github.com> --- .../database/mysql/monsterQueryService.go | 42 +++++++++++-------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/internal/database/mysql/monsterQueryService.go b/internal/database/mysql/monsterQueryService.go index c46692e7..50c2dc31 100644 --- a/internal/database/mysql/monsterQueryService.go +++ b/internal/database/mysql/monsterQueryService.go @@ -104,6 +104,7 @@ func (s *monsterQueryService) FetchList(ctx context.Context, id string) ([]*mons var err error where_clade := "" + whereArgs := []interface{}{} sort := "" if id == "" { @@ -116,38 +117,47 @@ func (s *monsterQueryService) FetchList(ctx context.Context, id string) ([]*mons if p.MonsterIds != "" { monsterIds = strings.Split(p.MonsterIds, ",") where_clade = "monster_id IN (?)" + whereArgs = append(whereArgs, monsterIds) } - if p.MonsterName != "" && p.MonsterIds != "" { - where_clade += " and name LIKE '%" + p.MonsterName + "%' " - } else if p.MonsterName != "" { - where_clade += " name LIKE '%" + p.MonsterName + "%' " + if p.MonsterName != "" { + if where_clade != "" { + where_clade += " and name LIKE ?" + } else { + where_clade = "name LIKE ?" + } + whereArgs = append(whereArgs, "%"+p.MonsterName+"%") } // Add usage element search if p.UsageElement != "" { if where_clade != "" { - where_clade += " and element = '" + p.UsageElement + "' " + where_clade += " and element = ?" } else { - where_clade += " element = '" + p.UsageElement + "' " + where_clade = "element = ?" } + whereArgs = append(whereArgs, p.UsageElement) } // Add weakness element search - need to join with Weakness table if p.WeaknessElement != "" { weaknessJoin := " EXISTS (SELECT 1 FROM weakness w WHERE w.monster_id = monster.monster_id AND " + - "(w.first_weak_element = '" + p.WeaknessElement + "' OR " + - "w.second_weak_element = '" + p.WeaknessElement + "' OR " + - "w.fire = '" + p.WeaknessElement + "' OR " + - "w.water = '" + p.WeaknessElement + "' OR " + - "w.lightning = '" + p.WeaknessElement + "' OR " + - "w.ice = '" + p.WeaknessElement + "' OR " + - "w.dragon = '" + p.WeaknessElement + "'))" + "(w.first_weak_element = ? OR " + + "w.second_weak_element = ? OR " + + "w.fire = ? OR " + + "w.water = ? OR " + + "w.lightning = ? OR " + + "w.ice = ? OR " + + "w.dragon = ?))" if where_clade != "" { where_clade += " and " + weaknessJoin } else { - where_clade += weaknessJoin + where_clade = weaknessJoin + } + // Add the same value 7 times for the 7 placeholders + for i := 0; i < 7; i++ { + whereArgs = append(whereArgs, p.WeaknessElement) } } @@ -159,10 +169,8 @@ func (s *monsterQueryService) FetchList(ctx context.Context, id string) ([]*mons if id != "" { result = CtxFromDB(ctx).WithContext(ctx).Model(&monster).Preload("Weakness").Preload("Field").Preload("Tribe").Preload("Product").Preload("Ranking").Preload("BGM").Where("monster_id = ? ", id).Find(&monster) - } else if where_clade != "" && p.MonsterIds != "" { - result = CtxFromDB(ctx).WithContext(ctx).Model(&monster).Preload("Weakness").Preload("Field").Preload("Tribe").Preload("Product").Preload("Ranking").Preload("BGM").Where(where_clade, monsterIds).Limit(limit).Offset(offset).Order(sort).Find(&monster) } else if where_clade != "" { - result = CtxFromDB(ctx).WithContext(ctx).Model(&monster).Preload("Weakness").Preload("Field").Preload("Tribe").Preload("Product").Preload("Ranking").Preload("BGM").Where(where_clade).Limit(limit).Offset(offset).Order(sort).Find(&monster) + result = CtxFromDB(ctx).WithContext(ctx).Model(&monster).Preload("Weakness").Preload("Field").Preload("Tribe").Preload("Product").Preload("Ranking").Preload("BGM").Where(where_clade, whereArgs...).Limit(limit).Offset(offset).Order(sort).Find(&monster) } else { result = CtxFromDB(ctx).WithContext(ctx).Model(&monster).Preload("Weakness").Preload("Field").Preload("Tribe").Preload("Product").Preload("Ranking").Preload("BGM").Limit(limit).Offset(offset).Order(sort).Find(&monster) }