diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..76cf5b7 --- /dev/null +++ b/.drone.yml @@ -0,0 +1,69 @@ +kind: pipeline +type: docker +name: deployment + +steps: + - name: check + image: alpine + commands: + - ls -la + - ls -la Dockerfile # 查看当前文件夹是否包含了Dockerfile + - name: publish + image: plugins/docker + settings: + username: + from_secret: nexus_username + password: + from_secret: nexus_password + pull: if-not-exists # 如果镜像不存在则拉取,免去每次都要重新下载 + dockerfile: Dockerfile + tags: latest + # you need insecure: true since we don't have a TLS certificate + insecure: true + registry: 192.168.31.104:8082 + repo: 192.168.31.104:8082/tiamo/user-point-management + volumes: # 将容器内目录挂载到宿主机,仓库需要开启Trusted设置 + - name: dockersock + path: /var/run/docker.sock + - name: deploy + pull: if-not-exists + image: appleboy/drone-ssh + environment: + Connection: + from_secret: connection + NEXUS_USER: + from_secret: nexus_username + NEXUS_PASSWORD: + from_secret: nexus_password + settings: + host: 192.168.31.225 + port: 22 + username: drone + password: dronepw + command_timeout: 2m + envs: [ NEXUS_USER, NEXUS_PASSWORD ] + script: + - echo $NEXUS_PASSWORD | docker login -u $NEXUS_USER --password-stdin 192.168.31.104:8082 + - docker pull 192.168.31.104:8082/tiamo/user-point-management + - echo 开始停止容器 + - docker stop user-point-management + - echo 开始强制清除停止容器 + - docker container prune -f + - echo 开始清除镜像 + - docker image prune -f + - echo 开始标签镜像 + - docker tag 192.168.31.104:8082/tiamo/user-point-management tiamo/user-point-management:latest + - echo 开始运行容器 + - docker run --name user-point-management -d -p 29029:5000 + -e Connection__UserPointManagement="Server=192.168.31.225;Port=5432;UserId=postgres;Password=postgres;Database=tiamo;" + tiamo/user-point-management + - echo 执行完成 + +volumes: + - name: dockersock + host: + path: /var/run/docker.sock + +trigger: + branch: + - master \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..a822e00 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,26 @@ +FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base + +WORKDIR /docker +EXPOSE 5000 + +FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build +WORKDIR /build +COPY . . +RUN dotnet restore src/UserPointManagement.Web/UserPointManagement.Web.csproj --configfile ./NuGet.Config +WORKDIR src/UserPointManagement.Web +RUN dotnet build UserPointManagement.Web.csproj -c Release + +FROM build AS publish +RUN dotnet publish UserPointManagement.Web.csproj -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . + +ENV ASPNETCORE_URLS http://*:5000 +ENV TZ Asia/Shanghai +ENV Connection__UserPointManagement="" + +ENTRYPOINT ["dotnet", "UserPointManagement.Web.dll"] + + diff --git a/NuGet.Config b/NuGet.Config new file mode 100644 index 0000000..a728892 --- /dev/null +++ b/NuGet.Config @@ -0,0 +1,7 @@ + + + + + + + diff --git a/README.md b/README.md new file mode 100644 index 0000000..793a0ac --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +## 会员积分管理系统 \ No newline at end of file diff --git a/UserPointManagement.sln b/UserPointManagement.sln index 217ae76..ed58511 100644 --- a/UserPointManagement.sln +++ b/UserPointManagement.sln @@ -18,6 +18,15 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UserPointManagement.Persist EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UserPointManagement.Application.Tests", "test\UserPointManagement.Application.Tests\UserPointManagement.Application.Tests.csproj", "{5770D3E5-9B3E-40FC-8209-62F0B4B45BD4}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{08F901D9-600A-4B4D-A942-AD2F61EDFE49}" + ProjectSection(SolutionItems) = preProject + Dockerfile = Dockerfile + README.md = README.md + NuGet.Config = NuGet.Config + .drone.yml = .drone.yml + .gitignore = .gitignore + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU diff --git a/src/UserPointManagement.Application/Services/UserPointService.cs b/src/UserPointManagement.Application/Services/UserPointService.cs index 21489c5..7e23d9f 100644 --- a/src/UserPointManagement.Application/Services/UserPointService.cs +++ b/src/UserPointManagement.Application/Services/UserPointService.cs @@ -8,7 +8,7 @@ namespace UserPointManagement.Application.Services; public interface IUserPointService { - Task> GetUserPoints(GetUserPointDto req); + Task> GetUserPoints(GetUserPointDto req); Task CreateUserPoint(CreateUserPointDto input); @@ -24,26 +24,39 @@ public class UserPointService : IUserPointService _dbContextFactory = dbContextFactory; } - public async Task> GetUserPoints(GetUserPointDto req) + public async Task> GetUserPoints(GetUserPointDto req) { await using var _userPointManagementDbContext = await _dbContextFactory.CreateDbContextAsync(); - var queryable = from userPoint in _userPointManagementDbContext.UserPoints - join user in _userPointManagementDbContext.Users on userPoint.UserId equals user.Id + + var queryable = _userPointManagementDbContext.Users + .Where(x => x.Id == req.UserId, req.UserId.HasValue); + + // 分页筛选只能针对用户 + var users = await queryable.OrderByDescending(x => x.Id).Paging(req).ToListAsync(); + + // 查出用户底下积分明细 + var userPoints = await (from userPoint in _userPointManagementDbContext.UserPoints + where users.Select(x => x.Id).Contains(userPoint.UserId) select new UserPointDto() { UserPointId = userPoint.Id, - UserId = user.Id, - Name = user.Name, + UserId = userPoint.UserId, Point = userPoint.Point, CreateTime = userPoint.CreateTime - }; + }).ToListAsync(); - queryable = queryable.Where(x => x.UserId == req.UserId, req.UserId.HasValue); + var data = userPoints.GroupBy(x => x.UserId) + .Select(x => new UserPointInfoDto() + { + UserId = x.Key, + Name = users.First(p => p.Id == x.Key).Name, + TotalPoint = x.Sum(p => p.Point), + Items = x.ToList() + }).ToList(); var count = queryable.Count(); - var data = await queryable.OrderByDescending(x => x.CreateTime).Paging(req).ToListAsync().ConfigureAwait(false); - return new PageResultDto() + return new PageResultDto() { Items = data, TotalCount = count diff --git a/src/UserPointManagement.Application/Services/UserService.cs b/src/UserPointManagement.Application/Services/UserService.cs index b7978ba..46b4921 100644 --- a/src/UserPointManagement.Application/Services/UserService.cs +++ b/src/UserPointManagement.Application/Services/UserService.cs @@ -37,7 +37,7 @@ public class UserService : IUserService queryable = queryable.Where(x => x.Mobile.Contains(req.Keyword) || x.Name.Contains(req.Keyword), !string.IsNullOrEmpty(req.Keyword)); - + var count = queryable.Count(); var data = await queryable.OrderByDescending(x => x.Id).Paging(req).ToListAsync().ConfigureAwait(false); @@ -63,7 +63,8 @@ public class UserService : IUserService public async Task CreateUser(User input) { await using var _userPointManagementDbContext = await _dbContextFactory.CreateDbContextAsync(); - if (_userPointManagementDbContext.Users.Any(x => x.Mobile == input.Mobile)) + if (!string.IsNullOrWhiteSpace(input.Mobile) && + _userPointManagementDbContext.Users.Any(x => x.Mobile == input.Mobile)) { throw new ArgumentException("手机号不可重复!"); } @@ -72,6 +73,7 @@ public class UserService : IUserService { throw new ArgumentException("名称不可重复!"); } + _userPointManagementDbContext.Users.Add(input); await _userPointManagementDbContext.SaveChangesAsync().ConfigureAwait(false); } diff --git a/src/UserPointManagement.Model/Dtos/User/GetUserDto.cs b/src/UserPointManagement.Model/Dtos/User/GetUserDto.cs index f263408..b0c866c 100644 --- a/src/UserPointManagement.Model/Dtos/User/GetUserDto.cs +++ b/src/UserPointManagement.Model/Dtos/User/GetUserDto.cs @@ -2,5 +2,5 @@ public class GetUserDto : PageBase { - public string Keyword { get; set; } + public string? Keyword { get; set; } } \ No newline at end of file diff --git a/src/UserPointManagement.Model/Dtos/UserPoint/UserPointDto.cs b/src/UserPointManagement.Model/Dtos/UserPoint/UserPointDto.cs index b1479b6..282260a 100644 --- a/src/UserPointManagement.Model/Dtos/UserPoint/UserPointDto.cs +++ b/src/UserPointManagement.Model/Dtos/UserPoint/UserPointDto.cs @@ -6,8 +6,6 @@ public class UserPointDto public int UserId { get; set; } - public string Name { get; set; } - public int Point { get; set; } public DateTime CreateTime { get; set; } diff --git a/src/UserPointManagement.Model/Dtos/UserPoint/UserPointInfoDto.cs b/src/UserPointManagement.Model/Dtos/UserPoint/UserPointInfoDto.cs new file mode 100644 index 0000000..d35c4a0 --- /dev/null +++ b/src/UserPointManagement.Model/Dtos/UserPoint/UserPointInfoDto.cs @@ -0,0 +1,12 @@ +namespace UserPointManagement.Model.Dtos.UserPoint; + +public class UserPointInfoDto +{ + public int UserId { get; set; } + + public string Name { get; set; } + + public int TotalPoint { get; set; } + + public List Items { get; set; } +} \ No newline at end of file diff --git a/src/UserPointManagement.Model/Entities/User.cs b/src/UserPointManagement.Model/Entities/User.cs index ca12ba6..6336605 100644 --- a/src/UserPointManagement.Model/Entities/User.cs +++ b/src/UserPointManagement.Model/Entities/User.cs @@ -6,5 +6,5 @@ public class User public string Name { get; set; } - public string Mobile { get; set; } + public string? Mobile { get; set; } } \ No newline at end of file diff --git a/src/UserPointManagement.Persistence/EntityTypeConfigurations/UserConfiguration.cs b/src/UserPointManagement.Persistence/EntityTypeConfigurations/UserConfiguration.cs index 473ad28..31329a3 100644 --- a/src/UserPointManagement.Persistence/EntityTypeConfigurations/UserConfiguration.cs +++ b/src/UserPointManagement.Persistence/EntityTypeConfigurations/UserConfiguration.cs @@ -9,10 +9,10 @@ public class UserConfiguration : IEntityTypeConfiguration public void Configure(EntityTypeBuilder builder) { builder.HasKey(e => e.Id); - + builder.Property(e => e.Id) .HasComment("主键"); - + builder.Property(e => e.Name) .HasComment("姓名") .HasMaxLength(50); diff --git a/src/UserPointManagement.Persistence/Migrations/20230730134035_ModifyUser.Designer.cs b/src/UserPointManagement.Persistence/Migrations/20230730134035_ModifyUser.Designer.cs new file mode 100644 index 0000000..2721dee --- /dev/null +++ b/src/UserPointManagement.Persistence/Migrations/20230730134035_ModifyUser.Designer.cs @@ -0,0 +1,89 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using UserPointManagement.Persistence; + +#nullable disable + +namespace UserPointManagement.Persistence.Migrations +{ + [DbContext(typeof(UserPointManagementDbContext))] + [Migration("20230730134035_ModifyUser")] + partial class ModifyUser + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "6.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("UserPointManagement.Model.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id") + .HasComment("主键"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Mobile") + .HasMaxLength(30) + .HasColumnType("character varying(30)") + .HasColumnName("mobile") + .HasComment("手机号"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("name") + .HasComment("姓名"); + + b.HasKey("Id") + .HasName("pk_user"); + + b.ToTable("user", (string)null); + }); + + modelBuilder.Entity("UserPointManagement.Model.Entities.UserPoint", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id") + .HasComment("主键"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreateTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("create_time") + .HasComment("新增时间"); + + b.Property("Point") + .HasColumnType("integer") + .HasColumnName("point") + .HasComment("积分"); + + b.Property("UserId") + .HasColumnType("integer") + .HasColumnName("user_id") + .HasComment("用户Id"); + + b.HasKey("Id") + .HasName("pk_user_point"); + + b.ToTable("user_point", (string)null); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/UserPointManagement.Persistence/Migrations/20230730134035_ModifyUser.cs b/src/UserPointManagement.Persistence/Migrations/20230730134035_ModifyUser.cs new file mode 100644 index 0000000..89f6326 --- /dev/null +++ b/src/UserPointManagement.Persistence/Migrations/20230730134035_ModifyUser.cs @@ -0,0 +1,41 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace UserPointManagement.Persistence.Migrations +{ + public partial class ModifyUser : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "mobile", + table: "user", + type: "character varying(30)", + maxLength: 30, + nullable: true, + comment: "手机号", + oldClrType: typeof(string), + oldType: "character varying(30)", + oldMaxLength: 30, + oldComment: "手机号"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "mobile", + table: "user", + type: "character varying(30)", + maxLength: 30, + nullable: false, + defaultValue: "", + comment: "手机号", + oldClrType: typeof(string), + oldType: "character varying(30)", + oldMaxLength: 30, + oldNullable: true, + oldComment: "手机号"); + } + } +} diff --git a/src/UserPointManagement.Persistence/Migrations/UserPointManagementDbContextModelSnapshot.cs b/src/UserPointManagement.Persistence/Migrations/UserPointManagementDbContextModelSnapshot.cs index ff15431..d3f4674 100644 --- a/src/UserPointManagement.Persistence/Migrations/UserPointManagementDbContextModelSnapshot.cs +++ b/src/UserPointManagement.Persistence/Migrations/UserPointManagementDbContextModelSnapshot.cs @@ -33,7 +33,6 @@ namespace UserPointManagement.Persistence.Migrations NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); b.Property("Mobile") - .IsRequired() .HasMaxLength(30) .HasColumnType("character varying(30)") .HasColumnName("mobile") diff --git a/src/UserPointManagement.Persistence/UserPointManagementDbContext.cs b/src/UserPointManagement.Persistence/UserPointManagementDbContext.cs index 7b7d34b..1ea298f 100644 --- a/src/UserPointManagement.Persistence/UserPointManagementDbContext.cs +++ b/src/UserPointManagement.Persistence/UserPointManagementDbContext.cs @@ -9,9 +9,9 @@ public class UserPointManagementDbContext : Microsoft.EntityFrameworkCore.DbCont public UserPointManagementDbContext(DbContextOptions options) : base(options) { } - + public virtual DbSet Users { get; set; } - + public virtual DbSet UserPoints { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) diff --git a/src/UserPointManagement.Persistence/UserPointManagementDbContextFactory.cs b/src/UserPointManagement.Persistence/UserPointManagementDbContextFactory.cs index 6edfa7e..9853b76 100644 --- a/src/UserPointManagement.Persistence/UserPointManagementDbContextFactory.cs +++ b/src/UserPointManagement.Persistence/UserPointManagementDbContextFactory.cs @@ -10,7 +10,7 @@ public class UserPointManagementDbContextFactory : IDesignTimeDbContextFactory(); optionsBuilder - .UseNpgsql("Server=67.230.184.225;Port=58007;UserId=postgres;Password=postgres;Database=tiamo;") + .UseNpgsql("Server=67.230.184.225;Port=58007;UserId=postgres;Password=postgres;Database=tiamo_dev;") .UseSnakeCaseNamingConvention(); AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true); diff --git a/src/UserPointManagement.Web/Controllers/UserController.cs b/src/UserPointManagement.Web/Controllers/UserController.cs new file mode 100644 index 0000000..e6296f6 --- /dev/null +++ b/src/UserPointManagement.Web/Controllers/UserController.cs @@ -0,0 +1,31 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using UserPointManagement.Application.Services; +using UserPointManagement.Model; +using UserPointManagement.Model.Dtos.User; +using UserPointManagement.Model.Entities; + +namespace UserPointManagement.Web.Controllers; + +[ApiController] +[Route("[controller]")] +public class UserController : ControllerBase +{ + private readonly IUserService _userService; + + public UserController(IUserService userService) + { + _userService = userService; + } + + /// + /// 获取用户 + /// + /// + /// + [HttpGet] + public async Task> GetUsers([FromQuery]GetUserDto req) + { + return await _userService.GetUsers(req).ConfigureAwait(false); + } +} \ No newline at end of file diff --git a/src/UserPointManagement.Web/MvcOptionsExtensions.cs b/src/UserPointManagement.Web/MvcOptionsExtensions.cs new file mode 100644 index 0000000..72e4d8e --- /dev/null +++ b/src/UserPointManagement.Web/MvcOptionsExtensions.cs @@ -0,0 +1,45 @@ +using System.Linq; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.ApplicationModels; +using Microsoft.AspNetCore.Mvc.Routing; + +namespace UserPointManagement.Web; + +public static class MvcOptionsExtensions +{ + private static void UseGeneralRoutePrefix(this MvcOptions opts, IRouteTemplateProvider routeAttribute) + { + opts.Conventions.Add(new RoutePrefixConvention(routeAttribute)); + } + + public static void UseGeneralRoutePrefix(this MvcOptions opts, string + prefix) + { + opts.UseGeneralRoutePrefix(new RouteAttribute(prefix)); + } +} + +public class RoutePrefixConvention : IApplicationModelConvention +{ + private readonly AttributeRouteModel _routePrefix; + + public RoutePrefixConvention(IRouteTemplateProvider route) + { + _routePrefix = new AttributeRouteModel(route); + } + + public void Apply(ApplicationModel application) + { + foreach (var selector in application.Controllers.SelectMany(c => c.Selectors)) + { + if (selector.AttributeRouteModel != null) + { + selector.AttributeRouteModel = AttributeRouteModel.CombineAttributeRouteModel(_routePrefix, selector.AttributeRouteModel); + } + else + { + selector.AttributeRouteModel = _routePrefix; + } + } + } +} \ No newline at end of file diff --git a/src/UserPointManagement.Web/Pages/UserManagement/UserManagement.razor b/src/UserPointManagement.Web/Pages/UserManagement/UserManagement.razor index 6ffe8c1..cb41f0f 100644 --- a/src/UserPointManagement.Web/Pages/UserManagement/UserManagement.razor +++ b/src/UserPointManagement.Web/Pages/UserManagement/UserManagement.razor @@ -92,7 +92,7 @@ - + diff --git a/src/UserPointManagement.Web/Pages/UserPointDetail/UserPointDetail.razor b/src/UserPointManagement.Web/Pages/UserPointDetail/UserPointDetail.razor index 4c5c626..60ab9d9 100644 --- a/src/UserPointManagement.Web/Pages/UserPointDetail/UserPointDetail.razor +++ b/src/UserPointManagement.Web/Pages/UserPointDetail/UserPointDetail.razor @@ -36,23 +36,36 @@ - - - - - - - - - - - + + + + + + + + +
+ + + + + + + + + + + +
+ @@ -68,7 +81,7 @@ @ref="@_form">