From 35c7d34af464eb29290b49a4314e4e19aec6ecd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=A6=E6=B1=89?= <5725748+qin_and_han_dynasties@user.noreply.gitee.com> Date: Fri, 27 Mar 2026 16:38:37 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BA=86=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E6=80=AA=E5=85=BD=E6=A8=A1=E5=9D=97=E7=9A=84=E5=90=8E=E5=8F=B0?= =?UTF-8?q?=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Frontend/src/api/monster.ts | 91 +++ .../Frontend/src/components/Sidebar.vue | 5 + .../Frontend/src/constants/theme.ts | 1 + .../Frontend/src/router/index.ts | 6 + .../Frontend/src/views/admin/MonstersView.vue | 658 ++++++++++++++++++ .../Controllers/MonsterController.cs | 100 +++ Build_God_Api/Build_God_Api/DB/Monster.cs | 52 ++ .../Build_God_Api/Dto/MonsterDtos.cs | 10 + Build_God_Api/Build_God_Api/Program.cs | 3 + .../Build_God_Api/Services/MonsterService.cs | 129 ++++ 10 files changed, 1055 insertions(+) create mode 100644 Build_God_Admin_Frontend/Frontend/src/api/monster.ts create mode 100644 Build_God_Admin_Frontend/Frontend/src/views/admin/MonstersView.vue create mode 100644 Build_God_Api/Build_God_Api/Controllers/MonsterController.cs create mode 100644 Build_God_Api/Build_God_Api/DB/Monster.cs create mode 100644 Build_God_Api/Build_God_Api/Dto/MonsterDtos.cs create mode 100644 Build_God_Api/Build_God_Api/Services/MonsterService.cs diff --git a/Build_God_Admin_Frontend/Frontend/src/api/monster.ts b/Build_God_Admin_Frontend/Frontend/src/api/monster.ts new file mode 100644 index 0000000..b3de5db --- /dev/null +++ b/Build_God_Admin_Frontend/Frontend/src/api/monster.ts @@ -0,0 +1,91 @@ +import http, { type EnumInfoDto } from "@/api/index"; + +export interface Monster { + id: number; + name: string; + description: string; + health: number; + attack: number; + defense: number; + criticalRate: number; + level: number; + type: number; + rewards?: MonsterReward[]; +} + +export interface MonsterReward { + id: number; + monsterId: number; + rewardType: number; + itemId: number; + itemName: string; + count: number; +} + +export interface PagedResult { + items: T[]; + totalCount: number; + pageNumber?: number; +} + +interface SearchMonsterDto { + pageNumber: number | undefined; + pageSize: number | undefined; + monsterType: number | undefined; + level: number | undefined; +} + +export const GetMonsterList = ( + monsterType?: number, + level?: number, + pageNumber?: number, + pageSize?: number +): Promise | Monster[]> => { + var dto: SearchMonsterDto = { + pageNumber: pageNumber, + pageSize: pageSize, + monsterType: monsterType, + level: level, + }; + return http.post("/monster/all", dto); +}; + +export const GetMonsterById = (id: number): Promise => { + return http.get(`/monster/${id}`); +}; + +export const AddMonster = (data: Monster): Promise => { + return http.post("/monster", data); +}; + +export const UpdateMonster = (data: Monster): Promise => { + return http.put(`/monster/${data.id}`, data); +}; + +export const DeleteMonster = (id: number): Promise => { + return http.delete(`/monster/${id}`); +}; + +export const GetMonsterTypes = (): Promise => { + return http.get("/monster/types"); +}; + +export const GetMonsterRewards = (monsterId: number): Promise => { + return http.get(`/monster/rewards/${monsterId}`); +}; + +export const GetRewardTypes = (): Promise => { + return http.get("/monster/reward-types"); +}; + +export const AddMonsterReward = (data: MonsterReward): Promise => { + return http.post("/monster/reward", data); +}; + +export const UpdateMonsterReward = (data: MonsterReward): Promise => { + return http.put(`/monster/reward/${data.id}`, data); +}; + +export const DeleteMonsterReward = (id: number): Promise => { + return http.delete(`/monster/reward/${id}`); +}; diff --git a/Build_God_Admin_Frontend/Frontend/src/components/Sidebar.vue b/Build_God_Admin_Frontend/Frontend/src/components/Sidebar.vue index a706a25..a67ef10 100644 --- a/Build_God_Admin_Frontend/Frontend/src/components/Sidebar.vue +++ b/Build_God_Admin_Frontend/Frontend/src/components/Sidebar.vue @@ -50,6 +50,11 @@ const menuItems = [ icon: ICONS.scrap, label: '垃圾管理', path: '/admin/scraps' + }, + { + icon: ICONS.monster, + label: '怪兽管理', + path: '/admin/monsters' } ] diff --git a/Build_God_Admin_Frontend/Frontend/src/constants/theme.ts b/Build_God_Admin_Frontend/Frontend/src/constants/theme.ts index 2944693..e9e31d0 100644 --- a/Build_God_Admin_Frontend/Frontend/src/constants/theme.ts +++ b/Build_God_Admin_Frontend/Frontend/src/constants/theme.ts @@ -7,6 +7,7 @@ export const ICONS = { bag:'🎒', mission: '📜', scrap: '📜', + monster: '👹', reward: { pill: '💊', diff --git a/Build_God_Admin_Frontend/Frontend/src/router/index.ts b/Build_God_Admin_Frontend/Frontend/src/router/index.ts index 407d7e7..11d5563 100644 --- a/Build_God_Admin_Frontend/Frontend/src/router/index.ts +++ b/Build_God_Admin_Frontend/Frontend/src/router/index.ts @@ -69,6 +69,12 @@ const routes: RouteRecordRaw[] = [ component: () => import('../views/admin/BagsView.vue'), meta: { title: '背包管理' } }, + { + path: 'monsters', + name: 'monsters', + component: () => import('../views/admin/MonstersView.vue'), + meta: { title: '怪兽管理' } + }, { path: 'settings', name: 'settings', diff --git a/Build_God_Admin_Frontend/Frontend/src/views/admin/MonstersView.vue b/Build_God_Admin_Frontend/Frontend/src/views/admin/MonstersView.vue new file mode 100644 index 0000000..ae93603 --- /dev/null +++ b/Build_God_Admin_Frontend/Frontend/src/views/admin/MonstersView.vue @@ -0,0 +1,658 @@ + + + + + diff --git a/Build_God_Api/Build_God_Api/Controllers/MonsterController.cs b/Build_God_Api/Build_God_Api/Controllers/MonsterController.cs new file mode 100644 index 0000000..09112fd --- /dev/null +++ b/Build_God_Api/Build_God_Api/Controllers/MonsterController.cs @@ -0,0 +1,100 @@ +using Build_God_Api.Common; +using Build_God_Api.DB; +using Build_God_Api.Dto; +using Build_God_Api.Services; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace Build_God_Api.Controllers +{ + [ApiController] + [Route("api/god/[controller]")] + public class MonsterController(IMonsterService service) : ControllerBase + { + private readonly IMonsterService _service = service; + + [HttpPost] + [Authorize(Roles = "admin")] + public async Task> Add([FromBody] Monster item) + { + return await _service.Add(item); + } + + [HttpPut("{id}")] + [Authorize(Roles = "admin")] + public async Task> Update(int id, [FromBody] Monster item) + { + item.Id = id; + return await _service.Update(item); + } + + [HttpDelete("{id}")] + [Authorize(Roles = "admin")] + public async Task> Delete(int id) + { + return await _service.Delete(id); + } + + [HttpGet("{id}")] + [Authorize] + public async Task> GetById(int id) + { + var monster = await _service.GetById(id); + if (monster == null) + { + return NotFound(); + } + return monster; + } + + [HttpPost("all")] + [Authorize] + public async Task>> GetAll([FromBody] SearchMonsterDto dto) + { + return await _service.GetAll(dto); + } + + [HttpGet("types")] + [Authorize] + public ActionResult> GetMonsterTypes() + { + return EnumHelper.GetEnumList(); + } + + [HttpGet("rewards/{monsterId}")] + [Authorize] + public async Task>> GetRewards(int monsterId) + { + return await _service.GetRewards(monsterId); + } + + [HttpPost("reward")] + [Authorize(Roles = "admin")] + public async Task> AddReward([FromBody] MonsterReward reward) + { + return await _service.AddReward(reward); + } + + [HttpPut("reward/{id}")] + [Authorize(Roles = "admin")] + public async Task> UpdateReward(int id, [FromBody] MonsterReward reward) + { + reward.Id = id; + return await _service.UpdateReward(reward); + } + + [HttpDelete("reward/{id}")] + [Authorize(Roles = "admin")] + public async Task> DeleteReward(int id) + { + return await _service.DeleteReward(id); + } + + [HttpGet("reward-types")] + [Authorize] + public ActionResult> GetRewardTypes() + { + return EnumHelper.GetEnumList(); + } + } +} diff --git a/Build_God_Api/Build_God_Api/DB/Monster.cs b/Build_God_Api/Build_God_Api/DB/Monster.cs new file mode 100644 index 0000000..44b286d --- /dev/null +++ b/Build_God_Api/Build_God_Api/DB/Monster.cs @@ -0,0 +1,52 @@ +using SqlSugar; +using System.ComponentModel; + +namespace Build_God_Api.DB +{ + public class Monster : BaseEntity + { + [SugarColumn(Length = 100)] + public string Name { get; set; } = string.Empty; + + [SugarColumn(Length = 500)] + public string Description { get; set; } = string.Empty; + + public int Health { get; set; } + + public int Attack { get; set; } + + public int Defense { get; set; } + + public decimal CriticalRate { get; set; } + + public int Level { get; set; } + + public MonsterType Type { get; set; } + } + + public class MonsterReward : BaseEntity + { + public int MonsterId { get; set; } + + public RewardType Type { get; set; } + + public int ItemId { get; set; } + + [SugarColumn(Length = 100)] + public string ItemName { get; set; } = string.Empty; + + public int Count { get; set; } + } + + public enum MonsterType + { + [Description("普通")] + Normal = 1, + + [Description("精英")] + Elite = 2, + + [Description("首领")] + Boss = 3 + } +} diff --git a/Build_God_Api/Build_God_Api/Dto/MonsterDtos.cs b/Build_God_Api/Build_God_Api/Dto/MonsterDtos.cs new file mode 100644 index 0000000..d19fb2b --- /dev/null +++ b/Build_God_Api/Build_God_Api/Dto/MonsterDtos.cs @@ -0,0 +1,10 @@ +namespace Build_God_Api.Dto +{ + public class SearchMonsterDto + { + public int PageNumber { get; set; } = 1; + public int PageSize { get; set; } = 10; + public int? MonsterType { get; set; } + public int? Level { get; set; } + } +} diff --git a/Build_God_Api/Build_God_Api/Program.cs b/Build_God_Api/Build_God_Api/Program.cs index c76baa1..2df4a95 100644 --- a/Build_God_Api/Build_God_Api/Program.cs +++ b/Build_God_Api/Build_God_Api/Program.cs @@ -97,6 +97,8 @@ namespace Build_God_Api sqlSugarClient.CodeFirst.InitTables(typeof(CharacterDailyMission)); sqlSugarClient.CodeFirst.InitTables(typeof(Scrap)); sqlSugarClient.CodeFirst.InitTables(typeof(CharacterScrap)); + sqlSugarClient.CodeFirst.InitTables(typeof(Monster)); + sqlSugarClient.CodeFirst.InitTables(typeof(MonsterReward)); return sqlSugarClient; }); @@ -156,6 +158,7 @@ namespace Build_God_Api builder.Services.AddHostedService(); builder.Services.AddScoped(); builder.Services.AddScoped(); + builder.Services.AddScoped(); builder.Services.AddCors(options => { diff --git a/Build_God_Api/Build_God_Api/Services/MonsterService.cs b/Build_God_Api/Build_God_Api/Services/MonsterService.cs new file mode 100644 index 0000000..380e4fb --- /dev/null +++ b/Build_God_Api/Build_God_Api/Services/MonsterService.cs @@ -0,0 +1,129 @@ +using Build_God_Api.DB; +using Build_God_Api.Dto; +using SqlSugar; + +namespace Build_God_Api.Services +{ + public interface IMonsterService + { + Task Add(Monster item); + + Task Delete(int id); + + Task Update(Monster item); + + Task GetById(int id); + + Task> GetAll(SearchMonsterDto dto); + + Task ExistsByNameAsync(string name); + + Task AddReward(MonsterReward reward); + + Task UpdateReward(MonsterReward reward); + + Task DeleteReward(int id); + + Task> GetRewards(int monsterId); + } + + public class MonsterService(ISqlSugarClient db, ICurrentUserService currentUserService) : IMonsterService + { + private readonly ISqlSugarClient _db = db; + private readonly ICurrentUserService _currentUserService = currentUserService; + + public async Task Add(Monster item) + { + var exists = await ExistsByNameAsync(item.Name); + if (exists) + { + throw new Exception($"已存在名为 {item.Name} 的怪兽"); + } + item.CreatedOn = DateTime.UtcNow; + item.CreatedBy = _currentUserService.UserId; + await _db.Insertable(item).ExecuteCommandAsync(); + return true; + } + + public async Task Delete(int id) + { + var monster = await _db.Queryable().FirstAsync(x => x.Id == id) + ?? throw new Exception("没找到对应的怪兽"); + + await _db.Deleteable().Where(x => x.MonsterId == id).ExecuteCommandAsync(); + await _db.Deleteable(monster).ExecuteCommandAsync(); + return true; + } + + public async Task ExistsByNameAsync(string name) + { + return await _db.Queryable().AnyAsync(x => x.Name == name); + } + + public async Task> GetAll(SearchMonsterDto dto) + { + var query = _db.Queryable() + .WhereIF(dto.MonsterType != null, x => (int)x.Type == dto.MonsterType) + .WhereIF(dto.Level != null, x => x.Level == dto.Level) + .OrderBy(x => x.CreatedOn, OrderByType.Desc); + + var list = await query.Skip((dto.PageNumber - 1) * dto.PageSize).Take(dto.PageSize).ToListAsync(); + var total = await query.CountAsync(); + + return new PagedResult + { + PageNumber = dto.PageNumber, + TotalCount = total, + Items = list + }; + } + + public async Task GetById(int id) + { + return await _db.Queryable().FirstAsync(x => x.Id == id); + } + + public async Task Update(Monster item) + { + var monster = await _db.Queryable().FirstAsync(x => x.Id == item.Id) + ?? throw new Exception("没找到对应的怪兽"); + item.UpdatedOn = DateTime.UtcNow; + item.UpdatedBy = _currentUserService.UserId; + await _db.Updateable(item).ExecuteCommandAsync(); + return true; + } + + public async Task AddReward(MonsterReward reward) + { + reward.CreatedOn = DateTime.UtcNow; + reward.CreatedBy = _currentUserService.UserId; + await _db.Insertable(reward).ExecuteCommandAsync(); + return true; + } + + public async Task UpdateReward(MonsterReward reward) + { + var existing = await _db.Queryable().FirstAsync(x => x.Id == reward.Id) + ?? throw new Exception("没找到对应的奖励"); + reward.UpdatedOn = DateTime.UtcNow; + reward.UpdatedBy = _currentUserService.UserId; + await _db.Updateable(reward).ExecuteCommandAsync(); + return true; + } + + public async Task DeleteReward(int id) + { + var reward = await _db.Queryable().FirstAsync(x => x.Id == id) + ?? throw new Exception("没找到对应的奖励"); + await _db.Deleteable(reward).ExecuteCommandAsync(); + return true; + } + + public async Task> GetRewards(int monsterId) + { + return await _db.Queryable() + .Where(x => x.MonsterId == monsterId) + .ToListAsync(); + } + } +}