diff --git a/Core/MAX.Framework/Commons/UnifyStatusCodeMiddleware.cs b/Core/MAX.Framework/Commons/UnifyStatusCodeMiddleware.cs
new file mode 100644
index 0000000..4c39f1d
--- /dev/null
+++ b/Core/MAX.Framework/Commons/UnifyStatusCodeMiddleware.cs
@@ -0,0 +1,189 @@
+using MAX.Framework.Dtos;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Logging;
+using Microsoft.IO;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Text.Json;
+
+namespace MAX.Framework.Commons
+{
+ ///
+ /// 统一状态码中间件
+ /// 统一返回200 固定格式
+ ///
+ internal class UnifyStatusCodeMiddleware
+ {
+
+ private readonly RequestDelegate _next;
+ private readonly ILogger _logger;
+
+ // 静态复用,全局唯一的内存池管理器
+ private static readonly RecyclableMemoryStreamManager _streamManager = new();
+
+ // 需要直接透传、不做缓冲的 Content-Type 前缀
+ private static readonly string[] _passthroughPrefixes =
+ {
+ "image/",
+ "audio/",
+ "video/",
+ "application/octet-stream",
+ "application/zip",
+ "application/pdf",
+ "application/x-msdownload",
+ "text/event-stream"
+ };
+
+ public UnifyStatusCodeMiddleware(RequestDelegate next, ILogger logger)
+ {
+ _next = next;
+ _logger = logger;
+ }
+
+ public async Task InvokeAsync(HttpContext context)
+ {
+ // 1. 只处理 /api 路径(按需调整或删除)
+ if (!context.Request.Path.StartsWithSegments("/api"))
+ {
+ await _next(context);
+ return;
+ }
+
+ // 2. 二进制 / 流式响应:不缓冲,直接透传
+ var accept = context.Request.Headers.Accept.ToString();
+ if (IsPassthroughByRequest(context) || IsPassthroughByAccept(accept))
+ {
+ await _next(context);
+ return;
+ }
+
+ var originalBody = context.Response.Body;
+ var buffer = _streamManager.GetStream();
+
+ context.Response.Body = buffer;
+
+ try
+ {
+ await _next(context);
+ }
+ catch (Exception ex)
+ {
+ // 兜底:Filter 没抓到的异常
+ _logger.LogError(ex, "未处理异常: {Path}", context.Request.Path);
+
+ // 清空已写入的内容,准备返回 500 统一格式
+ buffer.SetLength(0);
+ context.Response.StatusCode = StatusCodes.Status500InternalServerError;
+ }
+
+ var statusCode = context.Response.StatusCode;
+ var contentType = context.Response.ContentType ?? string.Empty;
+
+ // 3. 响应体如果本身就是二进制/流式,透传不处理
+ if (IsPassthroughByResponseContentType(contentType))
+ {
+ buffer.Position = 0;
+ context.Response.Body = originalBody;
+ await buffer.CopyToAsync(originalBody);
+ return;
+ }
+
+ // 4. 2xx 且已有响应体:透传(已被 UnifyResultFilter 包装过,不再包一层)
+ if (statusCode >= 200 && statusCode < 300 && buffer.Length > 0)
+ {
+ buffer.Position = 0;
+ context.Response.Body = originalBody;
+ await buffer.CopyToAsync(originalBody);
+ return;
+ }
+
+ // 5. 非 2xx:统一改写为 200 + ApiResult
+ var (code, msg) = MapStatusCode(statusCode);
+
+ // 如果是 2xx 但响应体为空(少见,例如 204),也当作成功
+ if (statusCode >= 200 && statusCode < 300)
+ {
+ code = (int)ResultCode.Success;
+ msg = "success";
+ }
+
+ var result = statusCode >= 200 && statusCode < 300
+ ? ApiResult.Success()
+ : ApiResult.Fail(msg, code);
+
+ // 恢复原始流并写出
+ context.Response.Body = originalBody;
+ context.Response.StatusCode = StatusCodes.Status200OK;
+ context.Response.ContentType = "application/json; charset=utf-8";
+ context.Response.ContentLength = null;
+
+ // 清理可能存在的旧响应头
+ context.Response.Headers.Remove("WWW-Authenticate");
+
+ await context.Response.WriteAsJsonAsync(result, new JsonSerializerOptions
+ {
+ PropertyNamingPolicy = JsonNamingPolicy.CamelCase
+ });
+
+ buffer.Dispose();
+ }
+
+ private static bool IsPassthroughByRequest(HttpContext context)
+ {
+ // 上传大文件等场景,请求体大,避免缓冲
+ var contentLength = context.Request.ContentLength;
+ if (contentLength.HasValue && contentLength.Value > 1_000_000) // >1MB
+ {
+ return true;
+ }
+
+ // Range 请求(断点续传)不缓冲
+ if (context.Request.Headers.ContainsKey("Range"))
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ private static bool IsPassthroughByAccept(string accept)
+ {
+ if (string.IsNullOrEmpty(accept)) return false;
+
+ foreach (var prefix in _passthroughPrefixes)
+ {
+ if (accept.Contains(prefix, StringComparison.OrdinalIgnoreCase))
+ return true;
+ }
+ return false;
+ }
+
+ private static bool IsPassthroughByResponseContentType(string contentType)
+ {
+ if (string.IsNullOrEmpty(contentType)) return false;
+
+ foreach (var prefix in _passthroughPrefixes)
+ {
+ if (contentType.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
+ return true;
+ }
+ return false;
+ }
+
+ private static (int code, string msg) MapStatusCode(int statusCode) => statusCode switch
+ {
+ 400 => ((int)ResultCode.ValidationError, "请求参数错误"),
+ 401 => ((int)ResultCode.Unauthorized, "未授权,请先登录"),
+ 403 => ((int)ResultCode.Forbidden, "禁止访问"),
+ 404 => ((int)ResultCode.NotFound, "资源不存在"),
+ 405 => ((int)ResultCode.Fail, "请求方法不允许"),
+ 429 => ((int)ResultCode.Fail, "请求过于频繁"),
+ 500 => ((int)ResultCode.ServerError, "服务器内部错误"),
+ 502 => ((int)ResultCode.ServerError, "网关错误"),
+ 503 => ((int)ResultCode.ServerError, "服务暂不可用"),
+ 504 => ((int)ResultCode.ServerError, "网关超时"),
+ _ => ((int)ResultCode.Fail, $"请求失败({statusCode})")
+ };
+ }
+}
diff --git a/Core/MAX.Framework/DependencyInjection/ServiceCollectionExtensions.cs b/Core/MAX.Framework/DependencyInjection/ServiceCollectionExtensions.cs
index a5aa210..4087f91 100644
--- a/Core/MAX.Framework/DependencyInjection/ServiceCollectionExtensions.cs
+++ b/Core/MAX.Framework/DependencyInjection/ServiceCollectionExtensions.cs
@@ -211,6 +211,7 @@ namespace MAX.Framework.DependencyInjection
public static IApplicationBuilder UseStore(this IApplicationBuilder app)
{
+ app.UseMiddleware();
foreach (IStartup startup in Startups)
{
startup.Start((app as IHost).Services);
diff --git a/Core/MAX.Framework/MAX.Framework.csproj b/Core/MAX.Framework/MAX.Framework.csproj
index 7d9b264..9c4469e 100644
--- a/Core/MAX.Framework/MAX.Framework.csproj
+++ b/Core/MAX.Framework/MAX.Framework.csproj
@@ -15,6 +15,7 @@
+
diff --git a/Directory.Build.props b/Directory.Build.props
index ba7e914..9b7dfdf 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -9,6 +9,7 @@
false
true
annotations
+ enable
CS1591
diff --git a/Models/CloudProduct/CloudProductModule.cs b/Modules/CloudProduct/CloudProductModule.cs
similarity index 82%
rename from Models/CloudProduct/CloudProductModule.cs
rename to Modules/CloudProduct/CloudProductModule.cs
index 2983fd5..f0759e7 100644
--- a/Models/CloudProduct/CloudProductModule.cs
+++ b/Modules/CloudProduct/CloudProductModule.cs
@@ -12,19 +12,11 @@ using Store.Modules.CloudProduct.Services;
namespace Store.Modules.CloudProduct
{
- ///
- /// CloudProduct 模块的模块化入口():Hangfire 后台任务(商品分词同步)+ Hangfire 看板。
- /// 与 (只做 IStartup 的 Initialize/Register)分开,是因为模块注册需要配置和 app:
- ///
- /// - builder.AddStoreHub() 执行 (注册 Hangfire 服务,此时只能拿到容器和配置);
- /// - app.UseStoreHubModules() 执行 (挂看板中间件 + 注册定时任务,此时才有 app)。
- ///
- ///
+
public class CloudProductModule : IModule
{
///
- /// 本模块自己的配置文件(源码里是 Models/CloudProduct/appsettings.json,
- /// 构建时复制到宿主输出目录并改名成这个名字,避免与宿主的 appsettings.json 冲突)
+ /// 本模块自己的配置文件
///
public const string ConfigurationFileName = "appsettings.CloudProduct.json";
@@ -51,8 +43,8 @@ namespace Store.Modules.CloudProduct
//// 本模块的配置节(Search / RawData / Indexing / Delete)都在模块自己的配置文件里,
//// 关键项缺失直接报错,避免 OpenSearch 地址为空、原始数据写到意外目录之类的隐性故障
RequireConfiguration(configuration, "Search:Url");
- //RequireConfiguration(configuration, "Search:IndexName");
- //RequireConfiguration(configuration, "RawData:Path");
+ RequireConfiguration(configuration, "Search:IndexName");
+ RequireConfiguration(configuration, "RawData:Path");
// ===== Hangfire 后台定时任务(商品分词同步) =====
var hangfireConnection = configuration.GetConnectionString("DefaultConnection")
diff --git a/Models/CloudProduct/Controllers/GoodsController.cs b/Modules/CloudProduct/Controllers/GoodsController.cs
similarity index 99%
rename from Models/CloudProduct/Controllers/GoodsController.cs
rename to Modules/CloudProduct/Controllers/GoodsController.cs
index 7565708..f7a2758 100644
--- a/Models/CloudProduct/Controllers/GoodsController.cs
+++ b/Modules/CloudProduct/Controllers/GoodsController.cs
@@ -13,7 +13,7 @@ using System.Text.Json.Nodes;
namespace Store.Modules.CloudProduct.Controllers
{
[ApiVersionNeutral]
- [Route("/CloudGoods/[action]")]
+ [Route("api/CloudGoods/[action]")]
public class GoodsController : AppControllerBase
{
private readonly IGoodsService _goodsService;
diff --git a/Models/CloudProduct/Dtos/GoodsImportItem.cs b/Modules/CloudProduct/Dtos/GoodsImportItem.cs
similarity index 100%
rename from Models/CloudProduct/Dtos/GoodsImportItem.cs
rename to Modules/CloudProduct/Dtos/GoodsImportItem.cs
diff --git a/Models/CloudProduct/Dtos/GoodsImportResult.cs b/Modules/CloudProduct/Dtos/GoodsImportResult.cs
similarity index 100%
rename from Models/CloudProduct/Dtos/GoodsImportResult.cs
rename to Modules/CloudProduct/Dtos/GoodsImportResult.cs
diff --git a/Models/CloudProduct/Dtos/GoodsSearchDocument.cs b/Modules/CloudProduct/Dtos/GoodsSearchDocument.cs
similarity index 100%
rename from Models/CloudProduct/Dtos/GoodsSearchDocument.cs
rename to Modules/CloudProduct/Dtos/GoodsSearchDocument.cs
diff --git a/Models/CloudProduct/Dtos/GoodsSearchRequest.cs b/Modules/CloudProduct/Dtos/GoodsSearchRequest.cs
similarity index 100%
rename from Models/CloudProduct/Dtos/GoodsSearchRequest.cs
rename to Modules/CloudProduct/Dtos/GoodsSearchRequest.cs
diff --git a/Models/CloudProduct/Dtos/OpenSearchStatusDto.cs b/Modules/CloudProduct/Dtos/OpenSearchStatusDto.cs
similarity index 100%
rename from Models/CloudProduct/Dtos/OpenSearchStatusDto.cs
rename to Modules/CloudProduct/Dtos/OpenSearchStatusDto.cs
diff --git a/Models/CloudProduct/Entities/GoodsEntity.cs b/Modules/CloudProduct/Entities/GoodsEntity.cs
similarity index 100%
rename from Models/CloudProduct/Entities/GoodsEntity.cs
rename to Modules/CloudProduct/Entities/GoodsEntity.cs
diff --git a/Models/CloudProduct/Enums/GoodsSearchSortBy.cs b/Modules/CloudProduct/Enums/GoodsSearchSortBy.cs
similarity index 100%
rename from Models/CloudProduct/Enums/GoodsSearchSortBy.cs
rename to Modules/CloudProduct/Enums/GoodsSearchSortBy.cs
diff --git a/Models/CloudProduct/Enums/PlatformType.cs b/Modules/CloudProduct/Enums/PlatformType.cs
similarity index 100%
rename from Models/CloudProduct/Enums/PlatformType.cs
rename to Modules/CloudProduct/Enums/PlatformType.cs
diff --git a/Models/CloudProduct/ImportConverters/GoodsConverter.cs b/Modules/CloudProduct/ImportConverters/GoodsConverter.cs
similarity index 100%
rename from Models/CloudProduct/ImportConverters/GoodsConverter.cs
rename to Modules/CloudProduct/ImportConverters/GoodsConverter.cs
diff --git a/Models/CloudProduct/ImportConverters/GoodsConverterFactory.cs b/Modules/CloudProduct/ImportConverters/GoodsConverterFactory.cs
similarity index 100%
rename from Models/CloudProduct/ImportConverters/GoodsConverterFactory.cs
rename to Modules/CloudProduct/ImportConverters/GoodsConverterFactory.cs
diff --git a/Models/CloudProduct/ImportConverters/PddGoodsConverter.cs b/Modules/CloudProduct/ImportConverters/PddGoodsConverter.cs
similarity index 100%
rename from Models/CloudProduct/ImportConverters/PddGoodsConverter.cs
rename to Modules/CloudProduct/ImportConverters/PddGoodsConverter.cs
diff --git a/Models/CloudProduct/Repositorys/GoodsRepository.cs b/Modules/CloudProduct/Repositorys/GoodsRepository.cs
similarity index 100%
rename from Models/CloudProduct/Repositorys/GoodsRepository.cs
rename to Modules/CloudProduct/Repositorys/GoodsRepository.cs
diff --git a/Models/CloudProduct/Services/GoodsIndexJob.cs b/Modules/CloudProduct/Services/GoodsIndexJob.cs
similarity index 100%
rename from Models/CloudProduct/Services/GoodsIndexJob.cs
rename to Modules/CloudProduct/Services/GoodsIndexJob.cs
diff --git a/Models/CloudProduct/Services/GoodsService.cs b/Modules/CloudProduct/Services/GoodsService.cs
similarity index 100%
rename from Models/CloudProduct/Services/GoodsService.cs
rename to Modules/CloudProduct/Services/GoodsService.cs
diff --git a/Models/CloudProduct/Services/OpenSearchService.cs b/Modules/CloudProduct/Services/OpenSearchService.cs
similarity index 100%
rename from Models/CloudProduct/Services/OpenSearchService.cs
rename to Modules/CloudProduct/Services/OpenSearchService.cs
diff --git a/Models/CloudProduct/Services/RawDataStorage.cs b/Modules/CloudProduct/Services/RawDataStorage.cs
similarity index 100%
rename from Models/CloudProduct/Services/RawDataStorage.cs
rename to Modules/CloudProduct/Services/RawDataStorage.cs
diff --git a/Models/CloudProduct/Startup.cs b/Modules/CloudProduct/Startup.cs
similarity index 100%
rename from Models/CloudProduct/Startup.cs
rename to Modules/CloudProduct/Startup.cs
diff --git a/Models/CloudProduct/Store.Modules.CloudProduct.csproj b/Modules/CloudProduct/Store.CloudProduct.csproj
similarity index 100%
rename from Models/CloudProduct/Store.Modules.CloudProduct.csproj
rename to Modules/CloudProduct/Store.CloudProduct.csproj
diff --git a/Models/CloudProduct/appsettings.json b/Modules/CloudProduct/appsettings.json
similarity index 81%
rename from Models/CloudProduct/appsettings.json
rename to Modules/CloudProduct/appsettings.json
index 2109cc1..06b88fb 100644
--- a/Models/CloudProduct/appsettings.json
+++ b/Modules/CloudProduct/appsettings.json
@@ -14,5 +14,11 @@
},
"Delete": {
"SoftDelete": true
+ },
+ "Hangfire": {
+ "Dashboard" :{
+ "User": "admin",
+ "Pass": "1234560"
+ }
}
}
diff --git a/Modules/Store.Shops.Contracts/ShopQueryService.cs b/Modules/Store.Shops.Contracts/ShopQueryService.cs
new file mode 100644
index 0000000..e3e13e7
--- /dev/null
+++ b/Modules/Store.Shops.Contracts/ShopQueryService.cs
@@ -0,0 +1,11 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Store.Shops.Contracts
+{
+ public interface IShopQueryService
+ {
+ DateTime GetExpireTime(string shopId);
+ }
+}
diff --git a/Modules/Store.Shops.Contracts/Store.Shops.Contracts.csproj b/Modules/Store.Shops.Contracts/Store.Shops.Contracts.csproj
new file mode 100644
index 0000000..fe64e56
--- /dev/null
+++ b/Modules/Store.Shops.Contracts/Store.Shops.Contracts.csproj
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/Modules/Store.Shops/Entities/ShopEntity.cs b/Modules/Store.Shops/Entities/ShopEntity.cs
new file mode 100644
index 0000000..d5ca731
--- /dev/null
+++ b/Modules/Store.Shops/Entities/ShopEntity.cs
@@ -0,0 +1,144 @@
+using FreeSql.DataAnnotations;
+using System;
+
+namespace YourNamespace
+{
+ ///
+ /// 店铺表
+ ///
+ [Table(Name = "wxxd.shop")]
+ public class ShopEntity
+ {
+ ///
+ /// 表id
+ ///
+ [Column(Name = "id", IsPrimary = true, IsIdentity = true)]
+ public int Id { get; set; }
+
+ ///
+ /// login_name
+ ///
+ [Column(Name = "login_name")]
+ public string LoginName { get; set; }
+
+ ///
+ /// login_pass
+ ///
+ [Column(Name = "login_pass")]
+ public string LoginPass { get; set; }
+
+ ///
+ /// cookie
+ ///
+ [Column(Name = "cookie")]
+ public string Cookie { get; set; }
+
+ ///
+ /// cookie2
+ ///
+ [Column(Name = "cookie2")]
+ public string Cookie2 { get; set; }
+
+ ///
+ /// anti_contents
+ ///
+ [Column(Name = "anti_contents")]
+ public string AntiContents { get; set; }
+
+ ///
+ /// openid
+ ///
+ [Column(Name = "openid")]
+ public string Openid { get; set; }
+
+ ///
+ /// shop_id
+ ///
+ [Column(Name = "shop_id")]
+ public string ShopId { get; set; }
+
+ ///
+ /// shop_account
+ ///
+ [Column(Name = "shop_account")]
+ public string ShopAccount { get; set; }
+
+ ///
+ /// sub_user_name
+ ///
+ [Column(Name = "sub_user_name")]
+ public string SubUserName { get; set; }
+
+ ///
+ /// user_name
+ ///
+ [Column(Name = "user_name")]
+ public string UserName { get; set; }
+
+ ///
+ /// 店铺分组名称(非子账号分组)
+ ///
+ [Column(Name = "group_name")]
+ public string GroupName { get; set; }
+
+ ///
+ /// add_time
+ ///
+ [Column(Name = "add_time")]
+ public DateTime? AddTime { get; set; }
+
+ ///
+ /// mdy_time
+ ///
+ [Column(Name = "mdy_time")]
+ public DateTime? MdyTime { get; set; }
+
+ ///
+ /// 店铺所属公司
+ ///
+ [Column(Name = "company_name")]
+ public string CompanyName { get; set; }
+
+ ///
+ /// 备注
+ ///
+ [Column(Name = "notes")]
+ public string Notes { get; set; }
+
+ ///
+ /// login_status
+ ///
+ [Column(Name = "login_status")]
+ public string LoginStatus { get; set; }
+
+ ///
+ /// 店铺每月商品上传限额剩余
+ ///
+ [Column(Name = "add_product_remaining")]
+ public int AddProductRemaining { get; set; }
+
+ ///
+ /// 上传限额所处月份
+ ///
+ [Column(Name = "add_product_month")]
+ public byte AddProductMonth { get; set; }
+
+ ///
+ /// 分配的二级超管的user_name
+ ///
+ [Column(Name = "operate_sub_sa_name")]
+ public string OperateSubSaName { get; set; }
+
+ ///
+ /// 分配二级超管的时间
+ ///
+ [Column(Name = "add_sub_sa_time")]
+ public DateTime? AddSubSaTime { get; set; }
+
+ ///
+ /// login_status2
+ ///
+ [Column(Name = "login_status2")]
+ public string LoginStatus2 { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/Modules/Store.Shops/Entities/ShopExpireEntity.cs b/Modules/Store.Shops/Entities/ShopExpireEntity.cs
new file mode 100644
index 0000000..919d4d1
--- /dev/null
+++ b/Modules/Store.Shops/Entities/ShopExpireEntity.cs
@@ -0,0 +1,43 @@
+using FreeSql.DataAnnotations;
+using System;
+
+namespace Store.Users.Entities
+{
+ ///
+ /// 商店过期
+ ///
+ [Table(Name = "wxxd.user_points_shop_expire")]
+ public class ShopExpireEntity
+ {
+ ///
+ /// ID
+ ///
+ [Column(Name = "id", IsPrimary = true, IsIdentity = true)]
+ public int Id { get; set; }
+
+ ///
+ /// 商店ID
+ ///
+ [Column(Name = "shop_id")]
+ public string ShopId { get; set; }
+
+ ///
+ /// 过期时间
+ ///
+ // 数据库列名末尾包含 U+200C,不能改成普通 expire_time。
+ [Column(Name = "expire_time")]
+ public int ExpireTime { get; set; }
+
+ ///
+ /// 添加时间
+ ///
+ [Column(Name = "add_time")]
+ public DateTime AddTime { get; set; }
+
+ ///
+ /// 修改时间
+ ///
+ [Column(Name = "mdy_time")]
+ public DateTime MdyTime { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/Modules/Store.Shops/Repositories/ShopRepository.cs b/Modules/Store.Shops/Repositories/ShopRepository.cs
new file mode 100644
index 0000000..8f8679c
--- /dev/null
+++ b/Modules/Store.Shops/Repositories/ShopRepository.cs
@@ -0,0 +1,47 @@
+using FreeSql;
+using MAX.Core.DependencyInjection;
+using MAX.Core.Helpers;
+using Store.Users.Entities;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using YourNamespace;
+
+namespace Store.Shops.Repositories
+{
+ public interface IShopRepository : IBaseRepository
+ {
+ ///
+ /// 获取用户商铺过期时间
+ ///
+ ///
+ ///
+ DateTime GetExpireTime(string ShopId);
+ }
+ internal class ShopRepository : BaseRepository, IShopRepository, ITransientDependency
+ {
+ private readonly IFreeSql _freeSql;
+
+ public ShopRepository(IFreeSql freeSql) : base(freeSql)
+ {
+ _freeSql = freeSql;
+ }
+
+ ///
+ /// 获取用户商铺过期时间
+ ///
+ ///
+ public DateTime GetExpireTime(string ShopId)
+ {
+ ShopExpireEntity currentExpire = _freeSql.Select()
+ .Where(a => a.ShopId == ShopId)
+ .ForUpdate()
+ .First();
+ if (currentExpire == null)
+ {
+ return DateTime.MinValue;
+ }
+ return DateHelper.ConvertFromUnixTimestampInSeconds(currentExpire.ExpireTime);
+ }
+ }
+}
diff --git a/Modules/Store.Shops/Services/ShopQueryService.cs b/Modules/Store.Shops/Services/ShopQueryService.cs
new file mode 100644
index 0000000..34415a4
--- /dev/null
+++ b/Modules/Store.Shops/Services/ShopQueryService.cs
@@ -0,0 +1,23 @@
+using MAX.Core.DependencyInjection;
+using Store.Shops.Contracts;
+using Store.Shops.Repositories;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Store.Shops.Services
+{
+ internal class ShopQueryService: IShopQueryService, ITransientDependency
+ {
+ private readonly IShopRepository _shopRepository;
+
+ public ShopQueryService(IShopRepository shopRepository)
+ {
+ _shopRepository = shopRepository;
+ }
+ public DateTime GetExpireTime(string shopId)
+ {
+ return _shopRepository.GetExpireTime(shopId);
+ }
+ }
+}
diff --git a/Modules/Store.Shops/Startup.cs b/Modules/Store.Shops/Startup.cs
new file mode 100644
index 0000000..0ec6974
--- /dev/null
+++ b/Modules/Store.Shops/Startup.cs
@@ -0,0 +1,25 @@
+using MAX.Core.DependencyInjection;
+using MAX.Core.Interfaces;
+
+namespace Store.Shops
+{
+ public class Startup : IStartup
+ {
+ public int Priority => IntPriority.Normal;
+
+ public void Initialize()
+ {
+
+ }
+
+ public void Register(IServiceRegistrar registrar)
+ {
+
+ }
+
+ public void Start(IServiceProvider serviceProvider)
+ {
+
+ }
+ }
+}
diff --git a/Modules/Store.Shops/Store.Shops.csproj b/Modules/Store.Shops/Store.Shops.csproj
new file mode 100644
index 0000000..1c57c72
--- /dev/null
+++ b/Modules/Store.Shops/Store.Shops.csproj
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/Modules/Store.Users.Contracts/Dtos/UserInfo.cs b/Modules/Store.Users.Contracts/Dtos/UserInfo.cs
new file mode 100644
index 0000000..1c9990f
--- /dev/null
+++ b/Modules/Store.Users.Contracts/Dtos/UserInfo.cs
@@ -0,0 +1,147 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations.Schema;
+using System.Text;
+
+namespace Store.Users.Contracts.Dtos
+{
+ public class UserInfo
+ {///
+ /// ID
+ ///
+ public int Id { get; set; }
+
+ ///
+ /// 用户昵称
+ ///
+ public string NickName { get; set; }
+
+ ///
+ /// 用户名称(登录账户)
+ ///
+ public string UserName { get; set; }
+
+ ///
+ /// 登录密码
+ ///
+ public string Password { get; set; }
+
+ ///
+ /// card_id
+ ///
+ public string CardId { get; set; }
+
+ ///
+ /// 用户分组 3主号 4客服 5加盟 8登录 9采购 10加盟 11上货
+ ///
+ public byte GroupId { get; set; }
+
+ ///
+ /// 父账户用户名
+ ///
+ public string ParentUserName { get; set; }
+
+ ///
+ /// oem编号,仅oem用户有值
+ ///
+ public string OemId { get; set; }
+
+ ///
+ /// 手机号
+ ///
+ public string Mobile { get; set; }
+
+ ///
+ /// 添加时间
+ ///
+ public DateTime? AddTime { get; set; }
+
+ ///
+ /// 修改时间
+ ///
+ public DateTime? MdyTime { get; set; }
+
+ ///
+ /// 公司名称
+ ///
+ public string CompanyName { get; set; }
+
+ ///
+ /// 备注
+ ///
+ public string Notes { get; set; }
+
+ ///
+ /// 超管等级 0 普通用户 1 超级管理员
+ ///
+ public short? SaLevel { get; set; }
+
+ ///
+ /// 账户权限
+ ///
+ public string Authority { get; set; }
+
+ ///
+ /// 子账号被禁止展示的功能页面
+ ///
+ public string ForbiddenPage { get; set; }
+
+ ///
+ /// 账户积分,1积分=1元
+ ///
+ public decimal Points { get; set; }
+
+ ///
+ /// 积分历史累计充值金额
+ ///
+ public decimal? RechargePointsTotal { get; set; }
+
+ ///
+ /// 店铺每续费一个月需要的积分,单位元
+ ///
+ public int ShopPointsPerMonth { get; set; }
+
+ ///
+ /// 可用余额
+ ///
+ public decimal? Balance { get; set; }
+
+ ///
+ /// 累计充值金额
+ ///
+ public decimal? RechargeAmountTotal { get; set; }
+
+ ///
+ /// 每单中转仓快递费用,单位元,两位小数
+ ///
+ public decimal? PostagePerOrder { get; set; }
+
+ ///
+ /// 每单中转仓退货快递费用,单位元,两位小数
+ ///
+ public decimal? RefundPostagePerOrder { get; set; }
+
+ ///
+ /// 礼物每单额外收取费用,单位元,两位小数
+ ///
+ public decimal? GiftOemAmountPerOrder { get; set; }
+
+
+ ///
+ /// 同步物流费用,单位元,两位小数
+ ///
+ public decimal? SyncLogisticsPerOrder { get; set; }
+
+ ///
+ /// 是否主账户
+ ///
+ public bool IsMainUser
+ {
+ get => string.IsNullOrWhiteSpace(ParentUserName);
+ }
+ ///
+ /// 是否OEM租户
+ ///
+ public bool IsOEMTenant => !string.IsNullOrWhiteSpace(OemId);
+ }
+}
diff --git a/Modules/Store.Users.Contracts/Enums/PaymentTypes.cs b/Modules/Store.Users.Contracts/Enums/PaymentTypes.cs
new file mode 100644
index 0000000..2de3b5e
--- /dev/null
+++ b/Modules/Store.Users.Contracts/Enums/PaymentTypes.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Store.Users.Contracts.Enums;
+///
+/// 用户付款类型
+///
+public enum UserPaymentTypes
+{
+ 充值 = 1,
+ //采购花费 = 2,
+ //产品退款到账 = 3,
+ //运费退款到账 = 4,
+ //采购收款 = 5,
+ //产品退款扣除 = 6,
+ //运费退款扣除 = 7,
+ 礼物单支出 = 16,
+ 中转仓扣费支出 = 20,
+ 中转仓作废返还收入 = 23,
+ 中转仓超重支出 = 24,
+ 退货包裹扣费支出 = 26,
+ 中转仓快递超重费用 = 28,
+ 支付宝在线充值 = 30,
+ 云仓退货支出 = 31,
+ 礼物单退费 = 34,
+ 同步单号 = 55,
+}
diff --git a/Modules/Store.Users.Contracts/IUserQueryService.cs b/Modules/Store.Users.Contracts/IUserQueryService.cs
new file mode 100644
index 0000000..f6808da
--- /dev/null
+++ b/Modules/Store.Users.Contracts/IUserQueryService.cs
@@ -0,0 +1,12 @@
+using Store.Users.Contracts.Dtos;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Store.Users.Contracts
+{
+ public interface IUserQueryService
+ {
+ Task GetUser(string userName);
+ }
+}
diff --git a/Modules/Store.Users.Contracts/Store.Users.Contracts.csproj b/Modules/Store.Users.Contracts/Store.Users.Contracts.csproj
new file mode 100644
index 0000000..5adef81
--- /dev/null
+++ b/Modules/Store.Users.Contracts/Store.Users.Contracts.csproj
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/Modules/Store.Users/Commands/ShopRenewCommand.cs b/Modules/Store.Users/Commands/ShopRenewCommand.cs
new file mode 100644
index 0000000..510050e
--- /dev/null
+++ b/Modules/Store.Users/Commands/ShopRenewCommand.cs
@@ -0,0 +1,81 @@
+//using MediatR;
+//using Store.Framework.Models;
+//using Store.Shops.Contracts;
+//using Store.Users.Contracts.Dtos;
+//using Store.Users.Entities;
+//using Store.Users.Repositories;
+//using System;
+//using System.Collections.Generic;
+//using System.Text;
+
+//namespace Store.Users.Commands
+//{
+// ///
+// /// 商铺续费
+// ///
+// ///
+// ///
+// ///
+// internal record class ShopRenewCommand(string UserName, string ShopId, int RenewDuration) : IRequest;
+// internal class ShopRenewCommandHandler : IRequestHandler
+// {
+// private readonly IUserRepository _userRepository;
+// private readonly IShopQueryService _shopQueryService;
+
+// public ShopRenewCommandHandler(IUserRepository userRepository, IShopQueryService shopQueryService)
+// {
+// _userRepository = userRepository;
+// _shopQueryService = shopQueryService;
+// }
+// async Task IRequestHandler.Handle(ShopRenewCommand request, CancellationToken cancellationToken)
+// {
+// ShopRenewResponse response = new ShopRenewResponse();
+// var user = await _userRepository.GetUser(request.UserName);
+// int shopPointsPerMonth = Math.Max(30, user.ShopPointsPerMonth);
+// int deltaPoints = shopPointsPerMonth * request.RenewDuration;
+// int newPoints = (int)user.Points - deltaPoints;
+// DateTime expireTime = _shopQueryService.GetExpireTime(request.ShopId);
+
+
+// if (currentExpire != null && currentExpire.ExpireTime > 0)
+// {
+// DateTime currentExpireTime = TimeUtils.StampToDateTime(currentExpire.ExpireTime);
+// if (currentExpireTime > expireBaseTime)
+// {
+// expireBaseTime = currentExpireTime;
+// }
+// }
+
+// int newExpireTime = CalculateNewExpireTime(expireBaseTime, request.RenewDuration);
+
+
+
+// response.ShopId = request.ShopId;
+// response.UserPoints = newPoints;
+// response.ExpireTime = newExpireTime;
+// return response;
+// }
+// private int CalculateNewExpireTime(DateTime expireBaseTime, int renewDuration)
+// {
+// DateTime newExpireDateTime;
+// try
+// {
+// newExpireDateTime = expireBaseTime.AddMonths(renewDuration);
+// }
+// catch (ArgumentOutOfRangeException)
+// {
+// throw new InvalidOperationException("续费时长超出允许范围");
+// }
+
+// if (newExpireDateTime > TimeUtils.StampToDateTime(int.MaxValue))
+// {
+// throw new InvalidOperationException("续费时间超出系统支持范围");
+// }
+
+// return TimeUtils.DateTimeToTimestampSeconds(newExpireDateTime);
+// }
+
+// }
+
+
+//}
diff --git a/Modules/Store.Users/Controllers/UserController.cs b/Modules/Store.Users/Controllers/UserController.cs
new file mode 100644
index 0000000..add205c
--- /dev/null
+++ b/Modules/Store.Users/Controllers/UserController.cs
@@ -0,0 +1,29 @@
+using Asp.Versioning;
+using Microsoft.AspNetCore.Mvc;
+using Store.Users.Contracts;
+using Store.Users.Contracts.Dtos;
+using Store.Users.Services;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Store.Users.Controllers
+{
+ [ApiController]
+ [ApiVersionNeutral]
+ [Route("user/[action]")]
+ public class UserController : ControllerBase
+ {
+ private readonly IUserQueryService _userQuery;
+
+ public UserController(IUserQueryService userQuery)
+ {
+ _userQuery = userQuery;
+ }
+ [HttpGet]
+ public async Task GetUser(string userName)
+ {
+ return await _userQuery.GetUser(userName);
+ }
+ }
+}
diff --git a/Modules/Store.Users/Dtos/ShopRenewRequest.cs b/Modules/Store.Users/Dtos/ShopRenewRequest.cs
new file mode 100644
index 0000000..7c4fa77
--- /dev/null
+++ b/Modules/Store.Users/Dtos/ShopRenewRequest.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Store.Users.Dtos
+{
+ ///
+ /// 用户续费商铺
+ ///
+ public class ShopRenewRequest
+ {
+ ///
+ /// 店铺ID
+ ///
+ public string ShopId { get; set; }
+
+ ///
+ /// 续费时长数值,例如 3;范围 1-120 个月
+ ///
+ public int RenewDuration { get; set; }
+
+ ///
+ /// 备注
+ ///
+ public string Remark { get; set; }
+ }
+}
diff --git a/Modules/Store.Users/Dtos/ShopRenewResponse.cs b/Modules/Store.Users/Dtos/ShopRenewResponse.cs
new file mode 100644
index 0000000..fc63cef
--- /dev/null
+++ b/Modules/Store.Users/Dtos/ShopRenewResponse.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Store.Users.Contracts.Dtos
+{
+ public class ShopRenewResponse
+ {
+ public string ShopId { get; set; }
+ public int ExpireTime { get; set; }
+ public int UserPoints { get; set; }
+ }
+}
diff --git a/Modules/Store.Users/Entities/OEMEntity.cs b/Modules/Store.Users/Entities/OEMEntity.cs
new file mode 100644
index 0000000..7ec4892
--- /dev/null
+++ b/Modules/Store.Users/Entities/OEMEntity.cs
@@ -0,0 +1,93 @@
+using FreeSql.DataAnnotations;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Web;
+
+namespace Store.Users.Entities;
+
+
+///
+/// OEM贴牌表
+///
+[Table(Name = "wxxd.oem")]
+public class OEMEntity
+{
+ ///
+ /// id
+ ///
+ [Column(Name = "id", IsPrimary = true, IsIdentity = true)]
+ public int Id { get; set; }
+
+ ///
+ /// oem_id
+ ///
+ [Column(Name = "oem_id")]
+ public string OemId { get; set; }
+
+ ///
+ /// 贴牌名称
+ ///
+ [Column(Name = "oem_name")]
+ public string OemName { get; set; }
+
+ ///
+ /// 贴牌管理人主账户
+ ///
+ [Column(Name = "oem_owner")]
+ public string OemOwner { get; set; }
+
+ ///
+ /// 图标
+ ///
+ [Column(Name = "oem_icon")]
+ public string OemIcon { get; set; }
+
+ ///
+ /// 题图
+ ///
+ [Column(Name = "oem_img")]
+ public string OemImg { get; set; }
+
+ ///
+ /// add_time
+ ///
+ [Column(Name = "add_time")]
+ public DateTime AddTime { get; set; }
+
+ ///
+ /// mdy_time
+ ///
+ [Column(Name = "mdy_time")]
+ public DateTime MdyTime { get; set; }
+
+ ///
+ /// is_del
+ ///
+ [Column(Name = "is_del")]
+ public byte IsDel { get; set; }
+
+ ///
+ /// 可提现余额,单位分
+ ///
+ [Column(Name = "available_balance")]
+ public long AvailableBalance { get; set; }
+
+ ///
+ /// 冻结余额,单位分
+ ///
+ [Column(Name = "frozen_balance")]
+ public long FrozenBalance { get; set; }
+
+ ///
+ /// 累计返利金额,单位分
+ ///
+ [Column(Name = "total_rebate_amount")]
+ public long TotalRebateAmount { get; set; }
+
+ ///
+ /// 累计提现金额,单位分
+ ///
+ [Column(Name = "total_withdraw_amount")]
+ public long TotalWithdrawAmount { get; set; }
+}
diff --git a/Modules/Store.Users/Entities/UserEntity.cs b/Modules/Store.Users/Entities/UserEntity.cs
new file mode 100644
index 0000000..0eb517b
--- /dev/null
+++ b/Modules/Store.Users/Entities/UserEntity.cs
@@ -0,0 +1,175 @@
+using FreeSql.DataAnnotations;
+using System;
+
+namespace Store.Users.Entities
+{
+ ///
+ /// 用户表
+ ///
+ [Table(Name = "wxxd.user")]
+ public class UserEntity
+ {
+ ///
+ /// ID
+ ///
+ [Column(Name = "id", IsPrimary = true, IsIdentity = true)]
+ public int Id { get; set; }
+
+ ///
+ /// 用户昵称
+ ///
+ [Column(Name = "nick_name")]
+ public string NickName { get; set; }
+
+ ///
+ /// 用户名称(登录账户)
+ ///
+ [Column(Name = "user_name")]
+ public string UserName { get; set; }
+
+ ///
+ /// 登录密码
+ ///
+ [Column(Name = "password")]
+ public string Password { get; set; }
+
+ ///
+ /// card_id
+ ///
+ [Column(Name = "card_id")]
+ public string CardId { get; set; }
+
+ ///
+ /// 用户分组 3主号 4客服 5加盟 8登录 9采购 10加盟 11上货
+ ///
+ [Column(Name = "group_id")]
+ public byte GroupId { get; set; }
+
+ ///
+ /// 父账户用户名
+ ///
+ [Column(Name = "parent_user_name")]
+ public string ParentUserName { get; set; }
+
+ ///
+ /// oem编号,仅oem用户有值
+ ///
+ [Column(Name = "oem_id")]
+ public string OemId { get; set; }
+
+ ///
+ /// 手机号
+ ///
+ [Column(Name = "mobile")]
+ public string Mobile { get; set; }
+
+ ///
+ /// 添加时间
+ ///
+ [Column(Name = "add_time")]
+ public DateTime? AddTime { get; set; }
+
+ ///
+ /// 修改时间
+ ///
+ [Column(Name = "mdy_time")]
+ public DateTime? MdyTime { get; set; }
+
+ ///
+ /// 公司名称
+ ///
+ [Column(Name = "company_name")]
+ public string CompanyName { get; set; }
+
+ ///
+ /// 备注
+ ///
+ [Column(Name = "notes")]
+ public string Notes { get; set; }
+
+ ///
+ /// 超管等级 0 普通用户 1 超级管理员
+ ///
+ [Column(Name = "sa_level")]
+ public short? SaLevel { get; set; }
+
+ ///
+ /// 账户权限
+ ///
+ [Column(Name = "authority")]
+ public string Authority { get; set; }
+
+ ///
+ /// 子账号被禁止展示的功能页面
+ ///
+ [Column(Name = "forbidden_page")]
+ public string ForbiddenPage { get; set; }
+
+ ///
+ /// 账户积分,1积分=1元
+ ///
+ [Column(Name = "points")]
+ public decimal Points { get; set; }
+
+ ///
+ /// 积分历史累计充值金额
+ ///
+ [Column(Name = "recharge_points_total")]
+ public decimal? RechargePointsTotal { get; set; }
+
+ ///
+ /// 店铺每续费一个月需要的积分,单位元
+ ///
+ [Column(Name = "shop_points_per_month")]
+ public int ShopPointsPerMonth { get; set; }
+
+ ///
+ /// 可用余额
+ ///
+ [Column(Name = "balance")]
+ public decimal? Balance { get; set; }
+
+ ///
+ /// 累计充值金额
+ ///
+ [Column(Name = "recharge_amount_total")]
+ public decimal? RechargeAmountTotal { get; set; }
+
+ ///
+ /// 每单中转仓快递费用,单位元,两位小数
+ ///
+ [Column(Name = "postage_per_order")]
+ public decimal? PostagePerOrder { get; set; }
+
+ ///
+ /// 每单中转仓退货快递费用,单位元,两位小数
+ ///
+ [Column(Name = "refund_postage_per_order")]
+ public decimal? RefundPostagePerOrder { get; set; }
+
+ ///
+ /// 礼物每单额外收取费用,单位元,两位小数
+ ///
+ [Column(Name = "gift_oem_amount_per_order")]
+ public decimal? GiftOemAmountPerOrder { get; set; }
+
+
+ ///
+ /// 同步物流费用,单位元,两位小数
+ ///
+ [Column(Name = "sync_logistics_per_order")]
+ public decimal? SyncLogisticsPerOrder { get; set; }
+
+ ///
+ /// 是否主账户
+ ///
+ public bool IsMainUser
+ {
+ get => string.IsNullOrWhiteSpace(ParentUserName);
+ }
+ ///
+ /// 是否OEM租户
+ ///
+ public bool IsOEMTenant => !string.IsNullOrWhiteSpace(OemId);
+ }
+}
\ No newline at end of file
diff --git a/Modules/Store.Users/Entities/UserPaymentRecordEntity.cs b/Modules/Store.Users/Entities/UserPaymentRecordEntity.cs
new file mode 100644
index 0000000..bb984f7
--- /dev/null
+++ b/Modules/Store.Users/Entities/UserPaymentRecordEntity.cs
@@ -0,0 +1,125 @@
+using FreeSql.DataAnnotations;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Web;
+
+namespace Store.Users.Entities;
+
+[Table(Name = "wxxd.user_payment_record")]
+public class UserPaymentRecordEntity
+{
+ ///
+ /// id
+ ///
+ [Column(Name = "id", IsPrimary = true, IsIdentity = true)]
+ public long Id { get; set; }
+
+ ///
+ /// 变动类型,1:充值,2:采购花费,3:产品退款到账,4:运费退款到账,5:采购收款,6:产品退款扣除,7:运费退款扣除, 16:礼物单话费,20:中转仓扣费支出,23:中转仓退款收入
+ ///
+ [Column(Name = "type")]
+ public int Type { get; set; }
+
+ ///
+ /// 金额(元)
+ ///
+ [Column(Name = "amount")]
+ public decimal? Amount { get; set; }
+
+ ///
+ /// 归属oem用户的金额
+ ///
+ [Column(Name = "oem_amount")]
+ public decimal? OemAmount { get; set; }
+
+ ///
+ /// 余额(元)
+ ///
+ [Column(Name = "balance")]
+ public decimal? Balance { get; set; }
+
+ ///
+ /// 账户名
+ ///
+ [Column(Name = "user_name")]
+ public string UserName { get; set; }
+
+ ///
+ /// 平台编码
+ ///
+ [Column(Name = "platform_code")]
+ public string PlatformCode { get; set; }
+
+ ///
+ /// 订单id
+ ///
+ [Column(Name = "order_id")]
+ public string OrderId { get; set; }
+
+ ///
+ /// 售后单id
+ ///
+ [Column(Name = "after_sale_id")]
+ public long? AfterSaleId { get; set; }
+
+ ///
+ /// 创建人
+ ///
+ [Column(Name = "create_by")]
+ public string CreateBy { get; set; }
+
+ ///
+ /// 创建时间
+ ///
+ [Column(Name = "create_time")]
+ public DateTime CreateTime { get; set; }
+
+ ///
+ /// 修改人
+ ///
+ [Column(Name = "update_by")]
+ public string UpdateBy { get; set; }
+
+ ///
+ /// 修改时间
+ ///
+ [Column(Name = "update_time")]
+ public DateTime? UpdateTime { get; set; }
+
+ ///
+ /// 是否已删除
+ ///
+ [Column(Name = "is_deleted")]
+ public int IsDeleted { get; set; }
+
+ ///
+ /// 备注
+ ///
+ [Column(Name = "remark")]
+ public string Remark { get; set; }
+
+ ///
+ /// 扣款的对方账户名称
+ ///
+ [Column(Name = "counterparty_account_name")]
+ public string CounterpartyAccountName { get; set; }
+
+ ///
+ /// OEM是否结算 0未结算 1已结算
+ ///
+ [Column(Name = "settlement")]
+ public byte Settlement { get; set; } = 0;
+
+ ///
+ /// 退款流水号 外采&订单号
+ ///
+ [Column(Name = "refund_flow_no")]
+ public string RefundFlowNo { get; set; }
+
+ ///
+ /// 结算时间
+ ///
+ [Column(Name = "settlement_time")]
+ public DateTime? SettlementTime { get; set; }
+}
\ No newline at end of file
diff --git a/Modules/Store.Users/Entities/UserPointsRecordEntity.cs b/Modules/Store.Users/Entities/UserPointsRecordEntity.cs
new file mode 100644
index 0000000..521e13a
--- /dev/null
+++ b/Modules/Store.Users/Entities/UserPointsRecordEntity.cs
@@ -0,0 +1,109 @@
+using FreeSql.DataAnnotations;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Web;
+
+namespace Store.Users.Entities
+{
+
+ [Table(Name = "wxxd.user_points_record")]
+ public class UserPointsRecordEntity
+ {
+ ///
+ /// 主键(自增)
+ ///
+ [Column(Name = "id", IsPrimary = true, IsIdentity = true)]
+ public long Id { get; set; }
+
+ ///
+ /// 用户名(NOT NULL)
+ ///
+ [Column(Name = "user_name")]
+ public string UserName { get; set; }
+
+ ///
+ /// 用户昵称(NOT NULL)
+ ///
+ [Column(Name = "user_nick")]
+ public string UserNick { get; set; }
+
+ ///
+ /// 操作记录金额,单位元,最小1元(NOT NULL)
+ ///
+ [Column(Name = "delta_points")]
+ public decimal DeltaPoints { get; set; }
+
+ ///
+ /// 充值或消费后的当前金额(允许 NULL)
+ ///
+ [Column(Name = "new_points")]
+ public decimal? NewPoints { get; set; }
+
+ ///
+ /// 操作类型 消费 充值 转入 转出(NOT NULL)
+ ///
+ [Column(Name = "option_type")]
+ public string OptionType { get; set; }
+
+ ///
+ /// 操作者类型 管理员 用户(NOT NULL)
+ ///
+ [Column(Name = "operator_type")]
+ public string OperatorType { get; set; }
+
+ ///
+ /// 实际操作者用户名(NOT NULL)
+ ///
+ [Column(Name = "operator_name")]
+ public string OperatorName { get; set; }
+
+ ///
+ /// 消费店铺编号(消费才有值,充值为空,允许 NULL)
+ ///
+ [Column(Name = "shop_id")]
+ public string? ShopId { get; set; }
+
+ ///
+ /// 消费店铺名称(消费才有值,充值为空,允许 NULL)
+ ///
+ [Column(Name = "shop_account")]
+ public string? ShopAccount { get; set; }
+
+ ///
+ /// 备注(允许 NULL)
+ ///
+ [Column(Name = "remark")]
+ public string? Remark { get; set; }
+
+ ///
+ /// (点数转出时的)对方账户(允许 NULL)
+ ///
+ [Column(Name = "counterparty_account_name")]
+ public string? CounterpartyAccountName { get; set; }
+
+ ///
+ /// 支付宝订单的外部订单号(允许 NULL)
+ ///
+ [Column(Name = "out_trade_no")]
+ public string? OutTradeNo { get; set; }
+
+ ///
+ /// 添加时间(NOT NULL,默认 CURRENT_TIMESTAMP)
+ ///
+ [Column(Name = "add_time")]
+ public DateTime AddTime { get; set; }
+
+ ///
+ /// OEM是否结算 0未结算 1已结算
+ ///
+ [Column(Name = "settlement")]
+ public bool Settlement { get; set; }
+ ///
+ /// 结算时间
+ ///
+ [Column(Name = "settlement_time")]
+ public DateTime? SettlementTime { get; set; }
+ }
+
+}
\ No newline at end of file
diff --git a/Modules/Store.Users/Mappers/UserMapper.cs b/Modules/Store.Users/Mappers/UserMapper.cs
new file mode 100644
index 0000000..b6c5071
--- /dev/null
+++ b/Modules/Store.Users/Mappers/UserMapper.cs
@@ -0,0 +1,15 @@
+using Riok.Mapperly.Abstractions;
+using Store.Users.Contracts.Dtos;
+using Store.Users.Entities;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Store.Users.Mappers
+{
+ [Mapper]
+ internal partial class UserMapper
+ {
+ public partial UserInfo ToInfo(UserEntity entity);
+ }
+}
diff --git a/Modules/Store.Users/Repositories/UserRepository.cs b/Modules/Store.Users/Repositories/UserRepository.cs
new file mode 100644
index 0000000..cbaf751
--- /dev/null
+++ b/Modules/Store.Users/Repositories/UserRepository.cs
@@ -0,0 +1,50 @@
+using FreeSql;
+using MAX.Core.DependencyInjection;
+using Store.Users.Contracts.Dtos;
+using Store.Users.Entities;
+using Store.Users.Mappers;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Store.Users.Repositories
+{
+ public interface IUserRepository : IBaseRepository
+ {
+ Task GetUser(string userName);
+ }
+ internal class UserRepository : BaseRepository, IUserRepository, ITransientDependency
+ {
+ private readonly IFreeSql _freeSql;
+
+ public UserRepository(IFreeSql freeSql) : base(freeSql)
+ {
+ _freeSql = freeSql;
+ }
+
+ public async Task GetUser(string userName)
+ {
+ var sql = Where(a => a.UserName == userName).ToSql();
+ var user = await Where(a => a.UserName == userName).FirstAsync();
+ if (user != null)
+ {
+ if (user.IsMainUser == false)
+ {
+ var mainUser = await Where(a => a.UserName == user.ParentUserName).FirstAsync();
+ if (mainUser != null)
+ {
+ user.OemId = mainUser.OemId;
+ user.PostagePerOrder = mainUser.PostagePerOrder;
+ user.RefundPostagePerOrder = mainUser.RefundPostagePerOrder;
+ user.GiftOemAmountPerOrder = mainUser.GiftOemAmountPerOrder;
+ user.SyncLogisticsPerOrder = mainUser.SyncLogisticsPerOrder;
+ }
+ }
+ }
+ return user;
+ }
+
+
+
+ }
+}
diff --git a/Modules/Store.Users/Services/UserQueryService.cs b/Modules/Store.Users/Services/UserQueryService.cs
new file mode 100644
index 0000000..6283be8
--- /dev/null
+++ b/Modules/Store.Users/Services/UserQueryService.cs
@@ -0,0 +1,43 @@
+using MAX.Core.DependencyInjection;
+using Store.Users.Contracts;
+using Store.Users.Contracts.Dtos;
+using Store.Users.Entities;
+using Store.Users.Mappers;
+using Store.Users.Repositories;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Store.Users.Services
+{
+ internal class UserQueryService : IUserQueryService, ITransientDependency
+ {
+ private readonly IUserRepository _userRepository;
+
+ public UserQueryService(IUserRepository userRepository)
+ {
+ _userRepository = userRepository;
+ }
+ public async Task GetUser(string userName)
+ {
+ var sql = _userRepository.Where(a => a.UserName == userName).ToSql();
+ var user = await _userRepository.Where(a => a.UserName == userName).FirstAsync();
+ if (user != null)
+ {
+ if (user.IsMainUser == false)
+ {
+ var mainUser = await _userRepository.Where(a => a.UserName == user.ParentUserName).FirstAsync();
+ if (mainUser != null)
+ {
+ user.OemId = mainUser.OemId;
+ user.PostagePerOrder = mainUser.PostagePerOrder;
+ user.RefundPostagePerOrder = mainUser.RefundPostagePerOrder;
+ user.GiftOemAmountPerOrder = mainUser.GiftOemAmountPerOrder;
+ user.SyncLogisticsPerOrder = mainUser.SyncLogisticsPerOrder;
+ }
+ }
+ }
+ return new UserMapper().ToInfo(user);
+ }
+ }
+}
diff --git a/Modules/Store.Users/Startup.cs b/Modules/Store.Users/Startup.cs
new file mode 100644
index 0000000..7cc1687
--- /dev/null
+++ b/Modules/Store.Users/Startup.cs
@@ -0,0 +1,22 @@
+using MAX.Core.DependencyInjection;
+using MAX.Core.Interfaces;
+
+namespace Store.Users
+{
+ public class Startup : IStartup
+ {
+ public int Priority => IntPriority.Normal;
+
+ public void Initialize()
+ {
+ }
+
+ public void Register(IServiceRegistrar registrar)
+ {
+ }
+
+ public void Start(IServiceProvider serviceProvider)
+ {
+ }
+ }
+}
diff --git a/Modules/Store.Users/Store.Users.csproj b/Modules/Store.Users/Store.Users.csproj
new file mode 100644
index 0000000..6402cfa
--- /dev/null
+++ b/Modules/Store.Users/Store.Users.csproj
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Store.API/Controllers/UserController.cs b/Store.API/Controllers/UserController.cs
index 4f11b90..f339489 100644
--- a/Store.API/Controllers/UserController.cs
+++ b/Store.API/Controllers/UserController.cs
@@ -7,21 +7,21 @@ using System.Text.Json.Serialization;
namespace Store.Api.Controllers
{
- [ApiController]
- [ApiVersionNeutral]
- [Route("wxxd_user/[action]")]
- public class UserController : ControllerBase
- {
- private readonly IUserRepository _userRepository;
+ //[ApiController]
+ //[ApiVersionNeutral]
+ //[Route("wxxd_user/[action]")]
+ //public class UserController : ControllerBase
+ //{
+ // private readonly IUserRepository _userRepository;
- public UserController(IUserRepository userRepository)
- {
- _userRepository = userRepository;
- }
- [HttpGet]
- public string GetUser()
- {
- return JsonSerializer.Serialize(_userRepository.Select.First());
- }
- }
+ // public UserController(IUserRepository userRepository)
+ // {
+ // _userRepository = userRepository;
+ // }
+ // [HttpGet]
+ // public string GetUser()
+ // {
+ // return JsonSerializer.Serialize(_userRepository.Select.First());
+ // }
+ //}
}
diff --git a/Store.API/Controllers/WeatherForecastController.cs b/Store.API/Controllers/WeatherForecastController.cs
deleted file mode 100644
index 872bf92..0000000
--- a/Store.API/Controllers/WeatherForecastController.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-using Asp.Versioning;
-using MAX.Framework.Dtos;
-using Microsoft.AspNetCore.Mvc;
-using Store.Api;
-using System.ComponentModel;
-
-namespace Store.Api.Controllers
-{
- [ApiController]
- [Description("测试温度")]
- [ApiVersion("1.0")]
- [Route("api/v{version:apiVersion}/[controller]")]
- public class WeatherForecastController : ControllerBase
- {
- private static readonly string[] Summaries =
- [
- "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
- ];
-
- [HttpGet(Name = "GetWeatherForecast")]
- public IEnumerable Get()
- {
- IEnumerable data = Enumerable.Range(1, 5).Select(index => new WeatherForecast
- {
- Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
- TemperatureC = Random.Shared.Next(-20, 55),
- Summary = Summaries[Random.Shared.Next(Summaries.Length)]
- })
- .ToArray();
-
- return data;
- }
- }
-}
diff --git a/Store.API/Controllers/v2/WeatherForecastController.cs b/Store.API/Controllers/v2/WeatherForecastController.cs
index af8af7c..795f172 100644
--- a/Store.API/Controllers/v2/WeatherForecastController.cs
+++ b/Store.API/Controllers/v2/WeatherForecastController.cs
@@ -5,29 +5,29 @@ using Store.Api;
namespace Store.Api.Controllers.v2
{
- [ApiController]
- [ApiVersion("2.0")]
- [Route("api/v{version:apiVersion}/[controller]")]
- public class WeatherForecastController : ControllerBase
- {
- private static readonly string[] Summaries =
- [
- "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
- ];
+ //[ApiController]
+ //[ApiVersion("2.0")]
+ //[Route("api/v{version:apiVersion}/[controller]")]
+ //public class WeatherForecastController : ControllerBase
+ //{
+ // private static readonly string[] Summaries =
+ // [
+ // "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
+ // ];
- [HttpGet(Name = "GetWeatherForecastV2")]
- public ApiResult> Get()
- {
- IEnumerable data = Enumerable.Range(1, 10).Select(index => new WeatherForecastV2
- {
- Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
- TemperatureC = Random.Shared.Next(-20, 55),
- FeelsLikeC = Random.Shared.Next(-20, 55),
- Summary = Summaries[Random.Shared.Next(Summaries.Length)]
- })
- .ToArray();
+ // [HttpGet(Name = "GetWeatherForecastV2")]
+ // public ApiResult> Get()
+ // {
+ // IEnumerable data = Enumerable.Range(1, 10).Select(index => new WeatherForecastV2
+ // {
+ // Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
+ // TemperatureC = Random.Shared.Next(-20, 55),
+ // FeelsLikeC = Random.Shared.Next(-20, 55),
+ // Summary = Summaries[Random.Shared.Next(Summaries.Length)]
+ // })
+ // .ToArray();
- return ApiResult>.Success(data);
- }
- }
+ // return ApiResult>.Success(data);
+ // }
+ //}
}
diff --git a/Store.API/Store.Api.csproj b/Store.API/Store.Api.csproj
index 42eff63..bf9f376 100644
--- a/Store.API/Store.Api.csproj
+++ b/Store.API/Store.Api.csproj
@@ -19,7 +19,8 @@
-
+
+
diff --git a/Store.API/WeatherForecast.cs b/Store.API/WeatherForecast.cs
deleted file mode 100644
index 542efad..0000000
--- a/Store.API/WeatherForecast.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-namespace Store.Api
-{
- public class WeatherForecast
- {
- public DateOnly Date { get; set; }
-
- public int TemperatureC { get; set; }
-
- public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
-
- public string? Summary { get; set; }
- }
-}
diff --git a/Store.API/WeatherForecastV2.cs b/Store.API/WeatherForecastV2.cs
deleted file mode 100644
index 058a9e1..0000000
--- a/Store.API/WeatherForecastV2.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-namespace Store.Api
-{
- public class WeatherForecastV2
- {
- public DateOnly Date { get; set; }
-
- public int TemperatureC { get; set; }
-
- public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
-
- public int FeelsLikeC { get; set; }
-
- public string? Summary { get; set; }
- }
-}
diff --git a/Store.Api.slnx b/Store.Api.slnx
index 79df559..27d1543 100644
--- a/Store.Api.slnx
+++ b/Store.Api.slnx
@@ -4,7 +4,11 @@
-
+
+
+
+
+
diff --git a/Store.Framework/Repositorys/SqlRepository.cs b/Store.Framework/Repositorys/SqlRepository.cs
index 36c06d1..e992e09 100644
--- a/Store.Framework/Repositorys/SqlRepository.cs
+++ b/Store.Framework/Repositorys/SqlRepository.cs
@@ -8,11 +8,8 @@ using System.Text;
namespace Store.Framework.Repositorys
{
- public interface IUserRepository : IBaseRepository
- {
- }
- public class SqlRepository : WxRepository, IUserRepository, IScopedDependency
+ public class SqlRepository : WxRepository, IScopedDependency
{
public SqlRepository([FromKeyedServices("KJDDB")] IFreeSql fsql) : base(fsql)
{