【新增】后端分发优惠券增加按用户组分发的队列功能。

This commit is contained in:
jianweie code
2024-07-31 00:34:29 +08:00
parent aa91837c55
commit 4de1ff6021
16 changed files with 355 additions and 40 deletions

View File

@@ -409,6 +409,11 @@ namespace CoreCms.Net.Configuration
//订单支付成功后,用户升级处理 //订单支付成功后,用户升级处理
public const string UserUpGrade = "UserUpGradeQueue"; public const string UserUpGrade = "UserUpGradeQueue";
/// <summary>
/// 优惠券发放给用户组队列
/// </summary>
public const string CouponDistributionSubscribe = "CouponDistributionSubscribe";
//消息相关 //消息相关
//发送微信模板消息 //发送微信模板消息

View File

@@ -948,6 +948,17 @@ namespace CoreCms.Net.Configuration
invalid = 3 invalid = 3
} }
/// <summary>
/// 优惠券分发方式
/// </summary>
public enum CouponDistributionMode
{
[Description("个人分发")]
Single = 1,
[Description("用户组分发")]
UserGroup = 2
}
#endregion #endregion
#region payments支付================================= #region payments支付=================================

View File

@@ -12,7 +12,12 @@ namespace CoreCms.Net.Core.Config
/// </summary> /// </summary>
public static class RedisMessageQueueSetup public static class RedisMessageQueueSetup
{ {
public static void AddRedisMessageQueueSetup(this IServiceCollection services) /// <summary>
/// 接口端消息队列
/// </summary>
/// <param name="services"></param>
/// <exception cref="ArgumentNullException"></exception>
public static void AddRedisMessageQueueSetupForApi(this IServiceCollection services)
{ {
if (services == null) throw new ArgumentNullException(nameof(services)); if (services == null) throw new ArgumentNullException(nameof(services));
@@ -46,5 +51,34 @@ namespace CoreCms.Net.Core.Config
m.ShowLog = false; m.ShowLog = false;
}); });
} }
/// <summary>
/// 管理端消息队列
/// </summary>
/// <param name="services"></param>
/// <exception cref="ArgumentNullException"></exception>
public static void AddRedisMessageQueueSetupForAdmin(this IServiceCollection services)
{
if (services == null) throw new ArgumentNullException(nameof(services));
services.AddInitQ(m =>
{
//没消息时挂起时长(毫秒)
m.SuspendTime = 1000;
//每次消费消息间隔时间(毫秒)
m.IntervalTime = 1000;
//redis服务器地址
m.ConnectionString = AppSettingsConstVars.RedisConfigConnectionString;
//对应的订阅者类需要new一个实例对象当然你也可以传参比如日志对象
m.ListSubscribe = new List<Type>() {
typeof(CouponDistributionSubscribe),
};
//显示日志
m.ShowLog = false;
});
}
} }
} }

View File

@@ -59,5 +59,13 @@ namespace CoreCms.Net.IRepository
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
Task<List<StatisticsOut>> StatisticsOrder(int day); Task<List<StatisticsOut>> StatisticsOrder(int day);
/// <summary>
/// 根据会员组获取会员id
/// </summary>
/// <returns></returns>
Task<List<int>> GetUserIdsByGrade(int gradeId);
} }
} }

View File

@@ -143,5 +143,10 @@ namespace CoreCms.Net.IServices
/// <returns></returns> /// <returns></returns>
Task<WebApiCallBack> InviteCommission(string orderId); Task<WebApiCallBack> InviteCommission(string orderId);
/// <summary>
/// 根据会员组获取会员id
/// </summary>
/// <returns></returns>
Task<List<int>> GetUserIdsByGrade(int gradeId);
} }
} }

View File

@@ -9941,6 +9941,31 @@
团购或秒杀传递的业务序列 团购或秒杀传递的业务序列
</summary> </summary>
</member> </member>
<member name="T:CoreCms.Net.Model.FromBody.FMCouponDistribution">
<summary>
优惠券分发
</summary>
</member>
<member name="P:CoreCms.Net.Model.FromBody.FMCouponDistribution.id">
<summary>
序列
</summary>
</member>
<member name="P:CoreCms.Net.Model.FromBody.FMCouponDistribution.distributionMode">
<summary>
分发方式
</summary>
</member>
<member name="P:CoreCms.Net.Model.FromBody.FMCouponDistribution.userIdOrMobile">
<summary>
用户序列
</summary>
</member>
<member name="P:CoreCms.Net.Model.FromBody.FMCouponDistribution.userGrade">
<summary>
用户组类别
</summary>
</member>
<member name="T:CoreCms.Net.Model.FromBody.FMReports"> <member name="T:CoreCms.Net.Model.FromBody.FMReports">
<summary> <summary>
后台查询报表综合提交参数 后台查询报表综合提交参数

View File

@@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CoreCms.Net.Model.FromBody
{
/// <summary>
/// 优惠券分发
/// </summary>
public class FMCouponDistribution
{
/// <summary>
/// 序列
/// </summary>
[Display(Name = "序列")]
[Required(ErrorMessage = "请提交优惠券序列")]
public int id { get; set; }
/// <summary>
/// 分发方式
/// </summary>
[Display(Name = "分发方式")]
[Required(ErrorMessage = "请提交优惠券分发方式")]
public int distributionMode { get; set; }
/// <summary>
/// 用户序列
/// </summary>
[Display(Name = "用户序列")]
[Required(ErrorMessage = "请提交优惠券用户序列")]
public long userIdOrMobile { get; set; } = 0;
/// <summary>
/// 用户组类别
/// </summary>
[Display(Name = "用户组类别")]
[Required(ErrorMessage = "请提交优惠券用户组类别")]
public int userGrade { get; set; } = 0;
}
}

View File

@@ -0,0 +1,101 @@
using System;
using System.Threading.Tasks;
using CoreCms.Net.Configuration;
using CoreCms.Net.IServices;
using CoreCms.Net.Loging;
using CoreCms.Net.Model.Entities;
using CoreCms.Net.Model.FromBody;
using InitQ.Abstractions;
using InitQ.Attributes;
using Newtonsoft.Json;
using NLog;
using SqlSugar;
namespace CoreCms.Net.RedisMQ
{
public class CouponDistributionSubscribe : IRedisSubscribe
{
private readonly ICoreCmsUserGradeServices _userGradeServices;
private readonly ICoreCmsUserServices _userServices;
private readonly ICoreCmsCouponServices _coreCmsCouponServices;
private readonly ICoreCmsPromotionServices _coreCmsPromotionServices;
/// <summary>
/// 构造函数
/// </summary>
public CouponDistributionSubscribe(ICoreCmsUserGradeServices userGradeServices, ICoreCmsUserServices userServices, ICoreCmsCouponServices coreCmsCouponServices, ICoreCmsPromotionServices coreCmsPromotionServices)
{
_userGradeServices = userGradeServices;
_userServices = userServices;
_coreCmsCouponServices = coreCmsCouponServices;
_coreCmsPromotionServices = coreCmsPromotionServices;
}
/// <summary>
/// 优惠券发放给用户组队列
/// </summary>
/// <param name="msg"></param>
/// <returns></returns>
[Subscribe(RedisMessageQueueKey.CouponDistributionSubscribe)]
private async Task CouponDistributionQueue(string msg)
{
try
{
var model = JsonConvert.DeserializeObject<FMCouponDistribution>(msg);
if (model.id == 0)
{
NLogUtil.WriteAll(NLog.LogLevel.Error, LogType.RedisMessageQueue, "优惠券发放给用户组队列", "优惠券获取失败" + msg);
await Task.CompletedTask;
}
var promotion = await _coreCmsPromotionServices.QueryByIdAsync(model.id);
if (promotion == null)
{
NLogUtil.WriteAll(NLog.LogLevel.Error, LogType.RedisMessageQueue, "优惠券发放给用户组队列", "优惠券获取失败" + msg);
await Task.CompletedTask;
}
var userGrade = _userGradeServices.QueryListByClauseAsync(p => p.id == model.userGrade);
if (userGrade == null)
{
NLogUtil.WriteAll(NLog.LogLevel.Error, LogType.RedisMessageQueue, "优惠券发放给用户组队列", "用户等级获取失败" + msg);
await Task.CompletedTask;
}
//获取用户id
var userIds = await _userServices.GetUserIdsByGrade(model.userGrade);
if (userIds.Count > 0)
{
var successCount = 0;
var errorCount = 0;
foreach (var id in userIds)
{
var result = await _coreCmsCouponServices.AddData(id, model.id, promotion);
if (result.status)
{
successCount++;
}
else
{
errorCount++;
}
}
NLogUtil.WriteAll(NLog.LogLevel.Info, LogType.RedisMessageQueue, $"优惠券发放给用户组队列", $"成功{successCount},失败{errorCount}" + msg);
}
else
{
NLogUtil.WriteAll(NLog.LogLevel.Info, LogType.RedisMessageQueue, "优惠券发放给用户组队列", "用户为空" + msg);
}
}
catch (Exception ex)
{
NLogUtil.WriteAll(NLog.LogLevel.Error, LogType.RedisMessageQueue, "优惠券发放给用户组队列", msg, ex);
throw;
}
await Task.CompletedTask;
}
}
}

View File

@@ -205,5 +205,18 @@ namespace CoreCms.Net.Repository
return outs; return outs;
} }
/// <summary>
/// 根据会员组获取会员id
/// </summary>
/// <returns></returns>
public async Task<List<int>> GetUserIdsByGrade(int gradeId)
{
var ids = await DbClient.Queryable<CoreCmsUser>().Where(p => p.grade == gradeId).Select(p => p.id)
.ToListAsync();
return ids;
}
} }
} }

View File

@@ -983,4 +983,16 @@ public class CoreCmsUserServices : BaseServices<CoreCmsUser>, ICoreCmsUserServic
} }
#endregion #endregion
/// <summary>
/// 根据会员组获取会员id
/// </summary>
/// <returns></returns>
public async Task<List<int>> GetUserIdsByGrade(int gradeId)
{
var ids = await _dal.GetUserIdsByGrade(gradeId);
return ids;
}
} }

View File

@@ -14,6 +14,7 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using CoreCms.Net.Caching.AutoMate.RedisCache;
using CoreCms.Net.Configuration; using CoreCms.Net.Configuration;
using CoreCms.Net.Filter; using CoreCms.Net.Filter;
using CoreCms.Net.IServices; using CoreCms.Net.IServices;
@@ -28,6 +29,7 @@ using CoreCms.Net.Web.Admin.Infrastructure;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using NPOI.HSSF.UserModel; using NPOI.HSSF.UserModel;
using SqlSugar; using SqlSugar;
@@ -55,7 +57,7 @@ namespace CoreCms.Net.Web.Admin.Controllers
private readonly ICoreCmsCouponServices _coreCmsCouponServices; private readonly ICoreCmsCouponServices _coreCmsCouponServices;
private readonly ICoreCmsUserServices _userServices; private readonly ICoreCmsUserServices _userServices;
private readonly ICoreCmsGoodsServices _goodsServices; private readonly ICoreCmsGoodsServices _goodsServices;
private readonly IRedisOperationRepository _redisOperationRepository;
/// <summary> /// <summary>
/// 构造函数 /// 构造函数
@@ -69,7 +71,7 @@ namespace CoreCms.Net.Web.Admin.Controllers
, ICoreCmsUserGradeServices coreCmsUserGradeServices , ICoreCmsUserGradeServices coreCmsUserGradeServices
, ICoreCmsPromotionConditionServices coreCmsPromotionConditionServices , ICoreCmsPromotionConditionServices coreCmsPromotionConditionServices
, ICoreCmsPromotionResultServices coreCmsPromotionResultServices , ICoreCmsPromotionResultServices coreCmsPromotionResultServices
, ICoreCmsCouponServices coreCmsCouponServices, ICoreCmsGoodsServices goodsServices, ICoreCmsUserServices userServices) , ICoreCmsCouponServices coreCmsCouponServices, ICoreCmsGoodsServices goodsServices, ICoreCmsUserServices userServices, IRedisOperationRepository redisOperationRepository)
{ {
_webHostEnvironment = webHostEnvironment; _webHostEnvironment = webHostEnvironment;
_coreCmsPromotionServices = coreCmsPromotionServices; _coreCmsPromotionServices = coreCmsPromotionServices;
@@ -83,6 +85,7 @@ namespace CoreCms.Net.Web.Admin.Controllers
_coreCmsCouponServices = coreCmsCouponServices; _coreCmsCouponServices = coreCmsCouponServices;
_goodsServices = goodsServices; _goodsServices = goodsServices;
_userServices = userServices; _userServices = userServices;
_redisOperationRepository = redisOperationRepository;
} }
#region ============================================================ #region ============================================================
@@ -438,11 +441,15 @@ namespace CoreCms.Net.Web.Admin.Controllers
jm.msg = "不存在此信息"; jm.msg = "不存在此信息";
return jm; return jm;
} }
var userGrade = await _coreCmsUserGradeServices.QueryAsync(false, true);
jm.code = 0; jm.code = 0;
jm.data = new jm.data = new
{ {
model model,
userGrade
}; };
return jm; return jm;
@@ -458,7 +465,7 @@ namespace CoreCms.Net.Web.Admin.Controllers
/// <returns></returns> /// <returns></returns>
[HttpPost] [HttpPost]
[Description("分发优惠券提交")] [Description("分发优惠券提交")]
public async Task<AdminUiCallBack> DoGrant([FromBody] FMIntId entity) public async Task<AdminUiCallBack> DoGrant([FromBody] FMCouponDistribution entity)
{ {
var jm = new AdminUiCallBack(); var jm = new AdminUiCallBack();
@@ -469,41 +476,42 @@ namespace CoreCms.Net.Web.Admin.Controllers
return jm; return jm;
} }
if (entity.data == null) //单人分发模式
if (entity.distributionMode == (int)GlobalEnumVars.CouponDistributionMode.Single)
{ {
jm.msg = "请输入合法的序列或手机号码"; if (entity.userIdOrMobile == 0)
return jm;
}
var userId = entity.data.ObjectToString();
CoreCmsUser user = null;
if (CommonHelper.IsMobile(userId))
{
user = await _userServices.QueryByClauseAsync(p => p.mobile == userId, true);
}
else
{
int id = 0;
var isInt = int.TryParse(userId, out id);
if (isInt && id > 0)
{ {
user = await _userServices.QueryByClauseAsync(p => p.id == id, true); jm.msg = "请输入合法的序列或手机号码";
return jm;
} }
CoreCmsUser user = null;
if (CommonHelper.IsMobile(entity.userIdOrMobile.ToString()))
{
user = await _userServices.QueryByClauseAsync(p => p.mobile == entity.userIdOrMobile.ToString(), true);
}
else
{
user = await _userServices.QueryByClauseAsync(p => p.id == entity.userIdOrMobile, true);
}
if (user == null)
{
jm.msg = "用户查询失败";
return jm;
}
var result = await _coreCmsCouponServices.AddData(user.id, entity.id, model);
jm.code = result.status ? 0 : 1;
jm.msg = result.status ? "发放成功" : "发放失败";
} }
if (user == null) // 用户组分发模式
else if (entity.distributionMode == (int)GlobalEnumVars.CouponDistributionMode.UserGroup)
{ {
jm.msg = "用户查询失败"; await _redisOperationRepository.ListLeftPushAsync(RedisMessageQueueKey.CouponDistributionSubscribe, JsonConvert.SerializeObject(entity));
return jm;
jm.code = 0;
jm.msg = "已经提交消息队列。";
} }
var result = await _coreCmsCouponServices.AddData(user.id, entity.id, model);
//事物处理过程结束
var bl = result.status;
jm.code = bl ? 0 : 1;
jm.msg = bl ? "发放成功" : "发放失败";
return jm; return jm;
} }
#endregion #endregion
@@ -859,7 +867,8 @@ namespace CoreCms.Net.Web.Admin.Controllers
{ {
jm.msg = GlobalErrorCodeVars.Code15016; jm.msg = GlobalErrorCodeVars.Code15016;
return jm; return jm;
} else if (model.type == (int)GlobalEnumVars.PromotionType.Coupon) }
else if (model.type == (int)GlobalEnumVars.PromotionType.Coupon)
{ {
jm.msg = GlobalErrorCodeVars.Code15030; jm.msg = GlobalErrorCodeVars.Code15030;
return jm; return jm;

View File

@@ -2955,7 +2955,7 @@
促销表 促销表
</summary> </summary>
</member> </member>
<member name="M:CoreCms.Net.Web.Admin.Controllers.CoreCmsPromotionController.#ctor(Microsoft.AspNetCore.Hosting.IWebHostEnvironment,CoreCms.Net.IServices.ICoreCmsPromotionServices,CoreCms.Net.IServices.ICoreCmsPromotionConditionServices,CoreCms.Net.IServices.ICoreCmsPromotionResultServices,CoreCms.Net.IServices.ICoreCmsGoodsCategoryServices,CoreCms.Net.IServices.ICoreCmsBrandServices,CoreCms.Net.IServices.ICoreCmsUserGradeServices,CoreCms.Net.IServices.ICoreCmsPromotionConditionServices,CoreCms.Net.IServices.ICoreCmsPromotionResultServices,CoreCms.Net.IServices.ICoreCmsCouponServices,CoreCms.Net.IServices.ICoreCmsGoodsServices,CoreCms.Net.IServices.ICoreCmsUserServices)"> <member name="M:CoreCms.Net.Web.Admin.Controllers.CoreCmsPromotionController.#ctor(Microsoft.AspNetCore.Hosting.IWebHostEnvironment,CoreCms.Net.IServices.ICoreCmsPromotionServices,CoreCms.Net.IServices.ICoreCmsPromotionConditionServices,CoreCms.Net.IServices.ICoreCmsPromotionResultServices,CoreCms.Net.IServices.ICoreCmsGoodsCategoryServices,CoreCms.Net.IServices.ICoreCmsBrandServices,CoreCms.Net.IServices.ICoreCmsUserGradeServices,CoreCms.Net.IServices.ICoreCmsPromotionConditionServices,CoreCms.Net.IServices.ICoreCmsPromotionResultServices,CoreCms.Net.IServices.ICoreCmsCouponServices,CoreCms.Net.IServices.ICoreCmsGoodsServices,CoreCms.Net.IServices.ICoreCmsUserServices,CoreCms.Net.Caching.AutoMate.RedisCache.IRedisOperationRepository)">
<summary> <summary>
构造函数 构造函数
</summary> </summary>
@@ -3006,7 +3006,7 @@
<param name="entity"></param> <param name="entity"></param>
<returns></returns> <returns></returns>
</member> </member>
<member name="M:CoreCms.Net.Web.Admin.Controllers.CoreCmsPromotionController.DoGrant(CoreCms.Net.Model.FromBody.FMIntId)"> <member name="M:CoreCms.Net.Web.Admin.Controllers.CoreCmsPromotionController.DoGrant(CoreCms.Net.Model.FromBody.FMCouponDistribution)">
<summary> <summary>
分发优惠券提交 分发优惠券提交
</summary> </summary>

View File

@@ -72,6 +72,9 @@ builder.Services.AddSingleton<CoreCms.Net.WeChat.Service.HttpClients.IWeChatApiH
//Swagger接口文档注入 //Swagger接口文档注入
builder.Services.AddAdminSwaggerSetup(); builder.Services.AddAdminSwaggerSetup();
//Redis消息队列
builder.Services.AddRedisMessageQueueSetupForAdmin();
//jwt授权支持注入 //jwt授权支持注入
builder.Services.AddAuthorizationSetupForAdmin(); builder.Services.AddAuthorizationSetupForAdmin();

View File

@@ -10,13 +10,35 @@
</div> </div>
<div class="layui-form-item"> <div class="layui-form-item">
<label class="layui-form-label">分发方式</label>
<div class="layui-input-inline layui-inline-7">
<input type="radio" lay-filter="distributionMode" name="distributionMode" value="1" title="个人分发" checked="checked">
<input type="radio" lay-filter="distributionMode" name="distributionMode" value="2" title="用户组分发">
</div>
<input type="hidden" id="signPointType" value="1">
</div>
<div class="layui-form-item distribution-user">
<label for="sort" class="layui-form-label">用户信息</label> <label for="sort" class="layui-form-label">用户信息</label>
<div class="layui-input-inline layui-inline-5"> <div class="layui-input-inline layui-inline-5">
<input type="number" min="0" max="999999" name="data" lay-verType="tips" lay-verify="required|number" class="layui-input" lay-reqText="请输入数字" /> <input type="number" min="0" max="999999" name="userIdOrMobile" class="layui-input" lay-reqText="请输入数字" />
</div> </div>
<div class="layui-form-mid layui-word-aux">请输入用户手机号码或者用户序列</div> <div class="layui-form-mid layui-word-aux">请输入用户手机号码或者用户序列</div>
</div> </div>
<div class="layui-form-item distribution-user-group">
<label for="sort" class="layui-form-label">用户组</label>
<div class="layui-input-inline layui-inline-5">
<select name="userGrade">
<option value="0" selected="selected">请选择</option>
{{# layui.each(d.params.data.userGrade, function(index, item){ }}
<option value="{{ item.id }}">{{ item.title }}</option>
{{# }); }}
</select>
</div>
<div class="layui-form-mid layui-word-aux">请选择用户级别</div>
</div>
<div class="layui-form-item core-hidden"> <div class="layui-form-item core-hidden">
<div class="layui-input-block"> <div class="layui-input-block">
<div class="layui-footer"> <div class="layui-footer">
@@ -42,6 +64,21 @@
, view = layui.view , view = layui.view
, coreHelper = layui.coreHelper; , coreHelper = layui.coreHelper;
//分发方式类型切换
$(".distribution-user").show();
$(".distribution-user-group").hide();
form.on('radio(distributionMode)', function (data) {
if (data.value == 1) {
$(".distribution-user").show();
$(".distribution-user-group").hide();
} else {
$(".distribution-user").hide();
$(".distribution-user-group").show();
}
});
//重载form //重载form
form.render(null, 'LAY-app-CoreCmsPromotion-grantForm'); form.render(null, 'LAY-app-CoreCmsPromotion-grantForm');
}) })

View File

@@ -278,7 +278,7 @@
admin.popup({ admin.popup({
shadeClose: false, shadeClose: false,
title: '分发优惠券', title: '分发优惠券',
area: ['600px', '300px'], area: ['600px', '500px'],
id: 'LAY-popup-CoreCmsPromotion-edit', id: 'LAY-popup-CoreCmsPromotion-edit',
success: function (layero, index) { success: function (layero, index) {
view(this.id).render('promotion/coupon/grant', { data: e.data }).done(function () { view(this.id).render('promotion/coupon/grant', { data: e.data }).done(function () {
@@ -287,6 +287,14 @@
function (data) { function (data) {
var field = data.field; //获取提交的字段 var field = data.field; //获取提交的字段
if (field.distributionMode == "2") {
field.userIdOrMobile = 0;
}
if (field.distributionMode == "1" && field.userIdOrMobile == "") {
layer.msg("请输入用户手机号码或者用户序列");
return false;
}
if (debug) { console.log(field); } //开启调试返回数据 if (debug) { console.log(field); } //开启调试返回数据
//提交 Ajax 成功后,关闭当前弹层并重载表格 //提交 Ajax 成功后,关闭当前弹层并重载表格
coreHelper.Post("Api/CoreCmsPromotion/DoGrant", field, function (e) { coreHelper.Post("Api/CoreCmsPromotion/DoGrant", field, function (e) {

View File

@@ -63,7 +63,7 @@ builder.Services.AddAutoMapper(typeof(AutoMapperConfiguration));
builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(typeof(TextMessageEventCommand).Assembly)); builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(typeof(TextMessageEventCommand).Assembly));
//Redis消息队列 //Redis消息队列
builder.Services.AddRedisMessageQueueSetup(); builder.Services.AddRedisMessageQueueSetupForApi();
// 引入Payment 依赖注入(支付宝支付/微信支付) // 引入Payment 依赖注入(支付宝支付/微信支付)
builder.Services.AddAlipay(); builder.Services.AddAlipay();