修改代码

增加云商品数据
This commit is contained in:
chfei committed 2026-10-14 11:53:36 +08:00
1 parent 2f406c840f
commit db4f030c17
58 files changed
+1550 -120

No files matched your search

@@ -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
{
/// <summary>
/// 统一状态码中间件
/// 统一返回200 固定格式
/// </summary>
internal class UnifyStatusCodeMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<UnifyStatusCodeMiddleware> _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<UnifyStatusCodeMiddleware> 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})")
};
}
}
@@ -211,6 +211,7 @@ namespace MAX.Framework.DependencyInjection
public static IApplicationBuilder UseStore(this IApplicationBuilder app)
{
app.UseMiddleware<UnifyStatusCodeMiddleware>();
foreach (IStartup startup in Startups)
{
startup.Start((app as IHost).Services);
+1
View File
@@ -15,6 +15,7 @@
<PackageReference Include="FreeSql.Provider.MySql" Version="3.5.311" />
<PackageReference Include="FreeSql.Repository" Version="3.5.311" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.10" />
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.1" />
<PackageReference Include="NLog.Web.AspNetCore" Version="6.2.0" />
<PackageReference Include="Riok.Mapperly" Version="4.3.1" />
</ItemGroup>
+1
View File
@@ -9,6 +9,7 @@
<CheckEolTargetFramework>false</CheckEolTargetFramework>
<SuppressNETCoreSdkPreviewMessage>true</SuppressNETCoreSdkPreviewMessage>
<Nullable>annotations</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<NoWarn>CS1591</NoWarn>
</PropertyGroup>
@@ -12,19 +12,11 @@ using Store.Modules.CloudProduct.Services;
namespace Store.Modules.CloudProduct
{
/// <summary>
/// CloudProduct 模块的模块化入口(<see cref="IModule"/>):Hangfire 后台任务(商品分词同步)+ Hangfire 看板。
/// 与 <see cref="Startup"/>(只做 IStartup 的 Initialize/Register)分开,是因为模块注册需要配置和 app:
/// <list type="bullet">
/// <item><c>builder.AddStoreHub()</c> 执行 <see cref="AddModule"/>(注册 Hangfire 服务,此时只能拿到容器和配置);</item>
/// <item><c>app.UseStoreHubModules()</c> 执行 <see cref="UseModule"/>(挂看板中间件 + 注册定时任务,此时才有 app)。</item>
/// </list>
/// </summary>
public class CloudProductModule : IModule
{
/// <summary>
/// 本模块自己的配置文件(源码里是 Models/CloudProduct/appsettings.json,
/// 构建时复制到宿主输出目录并改名成这个名字,避免与宿主的 appsettings.json 冲突)
/// 本模块自己的配置文件
/// </summary>
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")
@@ -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;
File renamed without changes.
@@ -14,5 +14,11 @@
},
"Delete": {
"SoftDelete": true
},
"Hangfire": {
"Dashboard" :{
"User": "admin",
"Pass": "1234560"
}
}
}
@@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Store.Shops.Contracts
{
public interface IShopQueryService
{
DateTime GetExpireTime(string shopId);
}
}
@@ -0,0 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
</PropertyGroup>
</Project>
+144
View File
@@ -0,0 +1,144 @@
using FreeSql.DataAnnotations;
using System;
namespace YourNamespace
{
/// <summary>
/// 店铺表
/// </summary>
[Table(Name = "wxxd.shop")]
public class ShopEntity
{
/// <summary>
/// 表id
/// </summary>
[Column(Name = "id", IsPrimary = true, IsIdentity = true)]
public int Id { get; set; }
/// <summary>
/// login_name
/// </summary>
[Column(Name = "login_name")]
public string LoginName { get; set; }
/// <summary>
/// login_pass
/// </summary>
[Column(Name = "login_pass")]
public string LoginPass { get; set; }
/// <summary>
/// cookie
/// </summary>
[Column(Name = "cookie")]
public string Cookie { get; set; }
/// <summary>
/// cookie2
/// </summary>
[Column(Name = "cookie2")]
public string Cookie2 { get; set; }
/// <summary>
/// anti_contents
/// </summary>
[Column(Name = "anti_contents")]
public string AntiContents { get; set; }
/// <summary>
/// openid
/// </summary>
[Column(Name = "openid")]
public string Openid { get; set; }
/// <summary>
/// shop_id
/// </summary>
[Column(Name = "shop_id")]
public string ShopId { get; set; }
/// <summary>
/// shop_account
/// </summary>
[Column(Name = "shop_account")]
public string ShopAccount { get; set; }
/// <summary>
/// sub_user_name
/// </summary>
[Column(Name = "sub_user_name")]
public string SubUserName { get; set; }
/// <summary>
/// user_name
/// </summary>
[Column(Name = "user_name")]
public string UserName { get; set; }
/// <summary>
/// 店铺分组名称(非子账号分组)
/// </summary>
[Column(Name = "group_name")]
public string GroupName { get; set; }
/// <summary>
/// add_time
/// </summary>
[Column(Name = "add_time")]
public DateTime? AddTime { get; set; }
/// <summary>
/// mdy_time
/// </summary>
[Column(Name = "mdy_time")]
public DateTime? MdyTime { get; set; }
/// <summary>
/// 店铺所属公司
/// </summary>
[Column(Name = "company_name")]
public string CompanyName { get; set; }
/// <summary>
/// 备注
/// </summary>
[Column(Name = "notes")]
public string Notes { get; set; }
/// <summary>
/// login_status
/// </summary>
[Column(Name = "login_status")]
public string LoginStatus { get; set; }
/// <summary>
/// 店铺每月商品上传限额剩余
/// </summary>
[Column(Name = "add_product_remaining")]
public int AddProductRemaining { get; set; }
/// <summary>
/// 上传限额所处月份
/// </summary>
[Column(Name = "add_product_month")]
public byte AddProductMonth { get; set; }
/// <summary>
/// 分配的二级超管的user_name
/// </summary>
[Column(Name = "operate_sub_sa_name")]
public string OperateSubSaName { get; set; }
/// <summary>
/// 分配二级超管的时间
/// </summary>
[Column(Name = "add_sub_sa_time")]
public DateTime? AddSubSaTime { get; set; }
/// <summary>
/// login_status2
/// </summary>
[Column(Name = "login_status2")]
public string LoginStatus2 { get; set; }
}
}
@@ -0,0 +1,43 @@
using FreeSql.DataAnnotations;
using System;
namespace Store.Users.Entities
{
/// <summary>
/// 商店过期
/// </summary>
[Table(Name = "wxxd.user_points_shop_expire")]
public class ShopExpireEntity
{
/// <summary>
/// ID
/// </summary>
[Column(Name = "id", IsPrimary = true, IsIdentity = true)]
public int Id { get; set; }
/// <summary>
/// 商店ID
/// </summary>
[Column(Name = "shop_id")]
public string ShopId { get; set; }
/// <summary>
/// 过期时间
/// </summary>
// 数据库列名末尾包含 U+200C,不能改成普通 expire_time。
[Column(Name = "expire_time")]
public int ExpireTime { get; set; }
/// <summary>
/// 添加时间
/// </summary>
[Column(Name = "add_time")]
public DateTime AddTime { get; set; }
/// <summary>
/// 修改时间
/// </summary>
[Column(Name = "mdy_time")]
public DateTime MdyTime { get; set; }
}
}
@@ -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<ShopEntity>
{
/// <summary>
/// 获取用户商铺过期时间
/// </summary>
/// <param name="ShopId"></param>
/// <returns></returns>
DateTime GetExpireTime(string ShopId);
}
internal class ShopRepository : BaseRepository<ShopEntity>, IShopRepository, ITransientDependency
{
private readonly IFreeSql _freeSql;
public ShopRepository(IFreeSql freeSql) : base(freeSql)
{
_freeSql = freeSql;
}
/// <summary>
/// 获取用户商铺过期时间
/// </summary>
/// <param name="ShopId"></param>
public DateTime GetExpireTime(string ShopId)
{
ShopExpireEntity currentExpire = _freeSql.Select<ShopExpireEntity>()
.Where(a => a.ShopId == ShopId)
.ForUpdate()
.First();
if (currentExpire == null)
{
return DateTime.MinValue;
}
return DateHelper.ConvertFromUnixTimestampInSeconds(currentExpire.ExpireTime);
}
}
}
@@ -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);
}
}
}
+25
View File
@@ -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)
{
}
}
}
+10
View File
@@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Store.Framework\Store.Framework.csproj" />
<ProjectReference Include="..\Store.Shops.Contracts\Store.Shops.Contracts.csproj" />
</ItemGroup>
</Project>
@@ -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
{/// <summary>
/// ID
/// </summary>
public int Id { get; set; }
/// <summary>
/// 用户昵称
/// </summary>
public string NickName { get; set; }
/// <summary>
/// 用户名称(登录账户)
/// </summary>
public string UserName { get; set; }
/// <summary>
/// 登录密码
/// </summary>
public string Password { get; set; }
/// <summary>
/// card_id
/// </summary>
public string CardId { get; set; }
/// <summary>
/// 用户分组 3主号 4客服 5加盟 8登录 9采购 10加盟 11上货
/// </summary>
public byte GroupId { get; set; }
/// <summary>
/// 父账户用户名
/// </summary>
public string ParentUserName { get; set; }
/// <summary>
/// oem编号,仅oem用户有值
/// </summary>
public string OemId { get; set; }
/// <summary>
/// 手机号
/// </summary>
public string Mobile { get; set; }
/// <summary>
/// 添加时间
/// </summary>
public DateTime? AddTime { get; set; }
/// <summary>
/// 修改时间
/// </summary>
public DateTime? MdyTime { get; set; }
/// <summary>
/// 公司名称
/// </summary>
public string CompanyName { get; set; }
/// <summary>
/// 备注
/// </summary>
public string Notes { get; set; }
/// <summary>
/// 超管等级 0 普通用户 1 超级管理员
/// </summary>
public short? SaLevel { get; set; }
/// <summary>
/// 账户权限
/// </summary>
public string Authority { get; set; }
/// <summary>
/// 子账号被禁止展示的功能页面
/// </summary>
public string ForbiddenPage { get; set; }
/// <summary>
/// 账户积分,1积分=1元
/// </summary>
public decimal Points { get; set; }
/// <summary>
/// 积分历史累计充值金额
/// </summary>
public decimal? RechargePointsTotal { get; set; }
/// <summary>
/// 店铺每续费一个月需要的积分,单位元
/// </summary>
public int ShopPointsPerMonth { get; set; }
/// <summary>
/// 可用余额
/// </summary>
public decimal? Balance { get; set; }
/// <summary>
/// 累计充值金额
/// </summary>
public decimal? RechargeAmountTotal { get; set; }
/// <summary>
/// 每单中转仓快递费用,单位元,两位小数
/// </summary>
public decimal? PostagePerOrder { get; set; }
/// <summary>
/// 每单中转仓退货快递费用,单位元,两位小数
/// </summary>
public decimal? RefundPostagePerOrder { get; set; }
/// <summary>
/// 礼物每单额外收取费用,单位元,两位小数
/// </summary>
public decimal? GiftOemAmountPerOrder { get; set; }
/// <summary>
/// 同步物流费用,单位元,两位小数
/// </summary>
public decimal? SyncLogisticsPerOrder { get; set; }
/// <summary>
/// 是否主账户
/// </summary>
public bool IsMainUser
{
get => string.IsNullOrWhiteSpace(ParentUserName);
}
/// <summary>
/// 是否OEM租户
/// </summary>
public bool IsOEMTenant => !string.IsNullOrWhiteSpace(OemId);
}
}
@@ -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;
/// <summary>
/// 用户付款类型
/// </summary>
public enum UserPaymentTypes
{
充值 = 1,
//采购花费 = 2,
//产品退款到账 = 3,
//运费退款到账 = 4,
//采购收款 = 5,
//产品退款扣除 = 6,
//运费退款扣除 = 7,
礼物单支出 = 16,
中转仓扣费支出 = 20,
中转仓作废返还收入 = 23,
中转仓超重支出 = 24,
退货包裹扣费支出 = 26,
中转仓快递超重费用 = 28,
支付宝在线充值 = 30,
云仓退货支出 = 31,
礼物单退费 = 34,
同步单号 = 55,
}
@@ -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<UserInfo> GetUser(string userName);
}
}
@@ -0,0 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
</PropertyGroup>
</Project>
@@ -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
//{
// /// <summary>
// /// 商铺续费
// /// </summary>
// /// <param name="UserName"></param>
// /// <param name="ShopId"></param>
// /// <param name="RenewDuration"></param>
// internal record class ShopRenewCommand(string UserName, string ShopId, int RenewDuration) : IRequest<ShopRenewResponse>;
// internal class ShopRenewCommandHandler : IRequestHandler<ShopRenewCommand, ShopRenewResponse>
// {
// private readonly IUserRepository _userRepository;
// private readonly IShopQueryService _shopQueryService;
// public ShopRenewCommandHandler(IUserRepository userRepository, IShopQueryService shopQueryService)
// {
// _userRepository = userRepository;
// _shopQueryService = shopQueryService;
// }
// async Task<ShopRenewResponse> IRequestHandler<ShopRenewCommand, ShopRenewResponse>.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);
// }
// }
//}
@@ -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<UserInfo> GetUser(string userName)
{
return await _userQuery.GetUser(userName);
}
}
}
@@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Store.Users.Dtos
{
/// <summary>
/// 用户续费商铺
/// </summary>
public class ShopRenewRequest
{
/// <summary>
/// 店铺ID
/// </summary>
public string ShopId { get; set; }
/// <summary>
/// 续费时长数值,例如 3;范围 1-120 个月
/// </summary>
public int RenewDuration { get; set; }
/// <summary>
/// 备注
/// </summary>
public string Remark { get; set; }
}
}
@@ -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; }
}
}
+93
View File
@@ -0,0 +1,93 @@
using FreeSql.DataAnnotations;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace Store.Users.Entities;
/// <summary>
/// OEM贴牌表
/// </summary>
[Table(Name = "wxxd.oem")]
public class OEMEntity
{
/// <summary>
/// id
/// </summary>
[Column(Name = "id", IsPrimary = true, IsIdentity = true)]
public int Id { get; set; }
/// <summary>
/// oem_id
/// </summary>
[Column(Name = "oem_id")]
public string OemId { get; set; }
/// <summary>
/// 贴牌名称
/// </summary>
[Column(Name = "oem_name")]
public string OemName { get; set; }
/// <summary>
/// 贴牌管理人主账户
/// </summary>
[Column(Name = "oem_owner")]
public string OemOwner { get; set; }
/// <summary>
/// 图标
/// </summary>
[Column(Name = "oem_icon")]
public string OemIcon { get; set; }
/// <summary>
/// 题图
/// </summary>
[Column(Name = "oem_img")]
public string OemImg { get; set; }
/// <summary>
/// add_time
/// </summary>
[Column(Name = "add_time")]
public DateTime AddTime { get; set; }
/// <summary>
/// mdy_time
/// </summary>
[Column(Name = "mdy_time")]
public DateTime MdyTime { get; set; }
/// <summary>
/// is_del
/// </summary>
[Column(Name = "is_del")]
public byte IsDel { get; set; }
/// <summary>
/// 可提现余额,单位分
/// </summary>
[Column(Name = "available_balance")]
public long AvailableBalance { get; set; }
/// <summary>
/// 冻结余额,单位分
/// </summary>
[Column(Name = "frozen_balance")]
public long FrozenBalance { get; set; }
/// <summary>
/// 累计返利金额,单位分
/// </summary>
[Column(Name = "total_rebate_amount")]
public long TotalRebateAmount { get; set; }
/// <summary>
/// 累计提现金额,单位分
/// </summary>
[Column(Name = "total_withdraw_amount")]
public long TotalWithdrawAmount { get; set; }
}
+175
View File
@@ -0,0 +1,175 @@
using FreeSql.DataAnnotations;
using System;
namespace Store.Users.Entities
{
/// <summary>
/// 用户表
/// </summary>
[Table(Name = "wxxd.user")]
public class UserEntity
{
/// <summary>
/// ID
/// </summary>
[Column(Name = "id", IsPrimary = true, IsIdentity = true)]
public int Id { get; set; }
/// <summary>
/// 用户昵称
/// </summary>
[Column(Name = "nick_name")]
public string NickName { get; set; }
/// <summary>
/// 用户名称(登录账户)
/// </summary>
[Column(Name = "user_name")]
public string UserName { get; set; }
/// <summary>
/// 登录密码
/// </summary>
[Column(Name = "password")]
public string Password { get; set; }
/// <summary>
/// card_id
/// </summary>
[Column(Name = "card_id")]
public string CardId { get; set; }
/// <summary>
/// 用户分组 3主号 4客服 5加盟 8登录 9采购 10加盟 11上货
/// </summary>
[Column(Name = "group_id")]
public byte GroupId { get; set; }
/// <summary>
/// 父账户用户名
/// </summary>
[Column(Name = "parent_user_name")]
public string ParentUserName { get; set; }
/// <summary>
/// oem编号,仅oem用户有值
/// </summary>
[Column(Name = "oem_id")]
public string OemId { get; set; }
/// <summary>
/// 手机号
/// </summary>
[Column(Name = "mobile")]
public string Mobile { get; set; }
/// <summary>
/// 添加时间
/// </summary>
[Column(Name = "add_time")]
public DateTime? AddTime { get; set; }
/// <summary>
/// 修改时间
/// </summary>
[Column(Name = "mdy_time")]
public DateTime? MdyTime { get; set; }
/// <summary>
/// 公司名称
/// </summary>
[Column(Name = "company_name")]
public string CompanyName { get; set; }
/// <summary>
/// 备注
/// </summary>
[Column(Name = "notes")]
public string Notes { get; set; }
/// <summary>
/// 超管等级 0 普通用户 1 超级管理员
/// </summary>
[Column(Name = "sa_level")]
public short? SaLevel { get; set; }
/// <summary>
/// 账户权限
/// </summary>
[Column(Name = "authority")]
public string Authority { get; set; }
/// <summary>
/// 子账号被禁止展示的功能页面
/// </summary>
[Column(Name = "forbidden_page")]
public string ForbiddenPage { get; set; }
/// <summary>
/// 账户积分,1积分=1元
/// </summary>
[Column(Name = "points")]
public decimal Points { get; set; }
/// <summary>
/// 积分历史累计充值金额
/// </summary>
[Column(Name = "recharge_points_total")]
public decimal? RechargePointsTotal { get; set; }
/// <summary>
/// 店铺每续费一个月需要的积分,单位元
/// </summary>
[Column(Name = "shop_points_per_month")]
public int ShopPointsPerMonth { get; set; }
/// <summary>
/// 可用余额
/// </summary>
[Column(Name = "balance")]
public decimal? Balance { get; set; }
/// <summary>
/// 累计充值金额
/// </summary>
[Column(Name = "recharge_amount_total")]
public decimal? RechargeAmountTotal { get; set; }
/// <summary>
/// 每单中转仓快递费用,单位元,两位小数
/// </summary>
[Column(Name = "postage_per_order")]
public decimal? PostagePerOrder { get; set; }
/// <summary>
/// 每单中转仓退货快递费用,单位元,两位小数
/// </summary>
[Column(Name = "refund_postage_per_order")]
public decimal? RefundPostagePerOrder { get; set; }
/// <summary>
/// 礼物每单额外收取费用,单位元,两位小数
/// </summary>
[Column(Name = "gift_oem_amount_per_order")]
public decimal? GiftOemAmountPerOrder { get; set; }
/// <summary>
/// 同步物流费用,单位元,两位小数
/// </summary>
[Column(Name = "sync_logistics_per_order")]
public decimal? SyncLogisticsPerOrder { get; set; }
/// <summary>
/// 是否主账户
/// </summary>
public bool IsMainUser
{
get => string.IsNullOrWhiteSpace(ParentUserName);
}
/// <summary>
/// 是否OEM租户
/// </summary>
public bool IsOEMTenant => !string.IsNullOrWhiteSpace(OemId);
}
}
@@ -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
{
/// <summary>
/// id
/// </summary>
[Column(Name = "id", IsPrimary = true, IsIdentity = true)]
public long Id { get; set; }
/// <summary>
/// 变动类型,1:充值,2:采购花费,3:产品退款到账,4:运费退款到账,5:采购收款,6:产品退款扣除,7:运费退款扣除, 16:礼物单话费,20:中转仓扣费支出,23:中转仓退款收入
/// </summary>
[Column(Name = "type")]
public int Type { get; set; }
/// <summary>
/// 金额(元)
/// </summary>
[Column(Name = "amount")]
public decimal? Amount { get; set; }
/// <summary>
/// 归属oem用户的金额
/// </summary>
[Column(Name = "oem_amount")]
public decimal? OemAmount { get; set; }
/// <summary>
/// 余额(元)
/// </summary>
[Column(Name = "balance")]
public decimal? Balance { get; set; }
/// <summary>
/// 账户名
/// </summary>
[Column(Name = "user_name")]
public string UserName { get; set; }
/// <summary>
/// 平台编码
/// </summary>
[Column(Name = "platform_code")]
public string PlatformCode { get; set; }
/// <summary>
/// 订单id
/// </summary>
[Column(Name = "order_id")]
public string OrderId { get; set; }
/// <summary>
/// 售后单id
/// </summary>
[Column(Name = "after_sale_id")]
public long? AfterSaleId { get; set; }
/// <summary>
/// 创建人
/// </summary>
[Column(Name = "create_by")]
public string CreateBy { get; set; }
/// <summary>
/// 创建时间
/// </summary>
[Column(Name = "create_time")]
public DateTime CreateTime { get; set; }
/// <summary>
/// 修改人
/// </summary>
[Column(Name = "update_by")]
public string UpdateBy { get; set; }
/// <summary>
/// 修改时间
/// </summary>
[Column(Name = "update_time")]
public DateTime? UpdateTime { get; set; }
/// <summary>
/// 是否已删除
/// </summary>
[Column(Name = "is_deleted")]
public int IsDeleted { get; set; }
/// <summary>
/// 备注
/// </summary>
[Column(Name = "remark")]
public string Remark { get; set; }
/// <summary>
/// 扣款的对方账户名称
/// </summary>
[Column(Name = "counterparty_account_name")]
public string CounterpartyAccountName { get; set; }
/// <summary>
/// OEM是否结算 0未结算 1已结算
/// </summary>
[Column(Name = "settlement")]
public byte Settlement { get; set; } = 0;
/// <summary>
/// 退款流水号 外采&订单号
/// </summary>
[Column(Name = "refund_flow_no")]
public string RefundFlowNo { get; set; }
/// <summary>
/// 结算时间
/// </summary>
[Column(Name = "settlement_time")]
public DateTime? SettlementTime { get; set; }
}
@@ -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
{
/// <summary>
/// 主键(自增)
/// </summary>
[Column(Name = "id", IsPrimary = true, IsIdentity = true)]
public long Id { get; set; }
/// <summary>
/// 用户名(NOT NULL)
/// </summary>
[Column(Name = "user_name")]
public string UserName { get; set; }
/// <summary>
/// 用户昵称(NOT NULL)
/// </summary>
[Column(Name = "user_nick")]
public string UserNick { get; set; }
/// <summary>
/// 操作记录金额,单位元,最小1元(NOT NULL)
/// </summary>
[Column(Name = "delta_points")]
public decimal DeltaPoints { get; set; }
/// <summary>
/// 充值或消费后的当前金额(允许 NULL)
/// </summary>
[Column(Name = "new_points")]
public decimal? NewPoints { get; set; }
/// <summary>
/// 操作类型 消费 充值 转入 转出(NOT NULL)
/// </summary>
[Column(Name = "option_type")]
public string OptionType { get; set; }
/// <summary>
/// 操作者类型 管理员 用户(NOT NULL)
/// </summary>
[Column(Name = "operator_type")]
public string OperatorType { get; set; }
/// <summary>
/// 实际操作者用户名(NOT NULL)
/// </summary>
[Column(Name = "operator_name")]
public string OperatorName { get; set; }
/// <summary>
/// 消费店铺编号(消费才有值,充值为空,允许 NULL)
/// </summary>
[Column(Name = "shop_id")]
public string? ShopId { get; set; }
/// <summary>
/// 消费店铺名称(消费才有值,充值为空,允许 NULL)
/// </summary>
[Column(Name = "shop_account")]
public string? ShopAccount { get; set; }
/// <summary>
/// 备注(允许 NULL)
/// </summary>
[Column(Name = "remark")]
public string? Remark { get; set; }
/// <summary>
/// (点数转出时的)对方账户(允许 NULL)
/// </summary>
[Column(Name = "counterparty_account_name")]
public string? CounterpartyAccountName { get; set; }
/// <summary>
/// 支付宝订单的外部订单号(允许 NULL)
/// </summary>
[Column(Name = "out_trade_no")]
public string? OutTradeNo { get; set; }
/// <summary>
/// 添加时间(NOT NULL,默认 CURRENT_TIMESTAMP)
/// </summary>
[Column(Name = "add_time")]
public DateTime AddTime { get; set; }
/// <summary>
/// OEM是否结算 0未结算 1已结算
/// </summary>
[Column(Name = "settlement")]
public bool Settlement { get; set; }
/// <summary>
/// 结算时间
/// </summary>
[Column(Name = "settlement_time")]
public DateTime? SettlementTime { get; set; }
}
}
+15
View File
@@ -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);
}
}
@@ -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<UserEntity>
{
Task<UserEntity> GetUser(string userName);
}
internal class UserRepository : BaseRepository<UserEntity>, IUserRepository, ITransientDependency
{
private readonly IFreeSql _freeSql;
public UserRepository(IFreeSql freeSql) : base(freeSql)
{
_freeSql = freeSql;
}
public async Task<UserEntity> 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;
}
}
}
@@ -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<UserInfo> 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);
}
}
}
+22
View File
@@ -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)
{
}
}
}
+13
View File
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Store.Framework\Store.Framework.csproj" />
<ProjectReference Include="..\Store.Shops.Contracts\Store.Shops.Contracts.csproj" />
<ProjectReference Include="..\Store.Users.Contracts\Store.Users.Contracts.csproj" />
</ItemGroup>
</Project>
+16 -16
View File
@@ -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());
// }
//}
}
@@ -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<WeatherForecast> Get()
{
IEnumerable<WeatherForecast> 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;
}
}
}
@@ -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<IEnumerable<WeatherForecastV2>> Get()
{
IEnumerable<WeatherForecastV2> 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<IEnumerable<WeatherForecastV2>> Get()
// {
// IEnumerable<WeatherForecastV2> 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<IEnumerable<WeatherForecastV2>>.Success(data);
}
}
// return ApiResult<IEnumerable<WeatherForecastV2>>.Success(data);
// }
//}
}
+2 -1
View File
@@ -19,7 +19,8 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Models\CloudProduct\Store.Modules.CloudProduct.csproj" />
<ProjectReference Include="..\Modules\CloudProduct\Store.CloudProduct.csproj" />
<ProjectReference Include="..\Modules\Store.Users\Store.Users.csproj" />
<ProjectReference Include="..\Store.Framework\Store.Framework.csproj" />
</ItemGroup>
-13
View File
@@ -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; }
}
}
-15
View File
@@ -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; }
}
}
+5 -1
View File
@@ -4,7 +4,11 @@
<Project Path="Core/MAX.Framework/MAX.Framework.csproj" />
</Folder>
<Folder Name="/Modules/">
<Project Path="Models/CloudProduct/Store.Modules.CloudProduct.csproj" Id="9720597a-661b-47d8-90c7-83e17c6d5920" />
<Project Path="Modules/CloudProduct/Store.CloudProduct.csproj" />
<Project Path="Modules/Store.Shops.Contracts/Store.Shops.Contracts.csproj" Id="7d313823-f86a-450a-be29-8f915fc4b5b3" />
<Project Path="Modules/Store.Shops/Store.Shops.csproj" Id="b1c06d62-9cfb-4e87-b495-7077dd0c088d" />
<Project Path="Modules/Store.Users.Contracts/Store.Users.Contracts.csproj" Id="d4a8177b-503c-4205-96ce-da0e01c855db" />
<Project Path="Modules/Store.Users/Store.Users.csproj" />
</Folder>
<Folder Name="/_SolutionItems/">
<File Path="Directory.Build.props" />
+1 -4
View File
@@ -8,11 +8,8 @@ using System.Text;
namespace Store.Framework.Repositorys
{
public interface IUserRepository : IBaseRepository<User>
{
}
public class SqlRepository : WxRepository<User>, IUserRepository, IScopedDependency
public class SqlRepository : WxRepository<User>, IScopedDependency
{
public SqlRepository([FromKeyedServices("KJDDB")] IFreeSql fsql) : base(fsql)
{