You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Nopcommerce 4.5插件开发:为特定客户角色禁用奖励积分

开发Nopcommerce插件:特定客户角色禁用奖励积分

以下是针对需求的分步实现方案,全程遵循Nopcommerce插件开发规范,不修改核心代码:

1. 搭建插件基础结构

创建名为Nop.Plugin.RewardPoints.DisableByCustomerRole的插件项目,包含核心文件:

  • plugin.json:配置插件名称、版本、依赖等元数据
  • 继承BasePlugin的主插件类(处理安装/卸载逻辑)
  • 自定义设置类(存储禁用积分的角色ID)
  • 管理端视图与模型

2. 定义插件设置

2.1 创建设置实体

用于持久化选中的禁用角色ID:

public class RewardPointsDisableByRoleSettings : ISettings
{
    public int[] DisabledCustomerRoleIds { get; set; } = Array.Empty<int>();
}

2.2 注册设置

在主插件类的InstallAsync方法中注册设置:

public override async Task InstallAsync()
{
    await _settingService.SaveSettingAsync(new RewardPointsDisableByRoleSettings());
    await base.InstallAsync();
}

3. 扩展奖励积分设置页面

通过Widget插件将角色选择控件嵌入原有设置页面,无需修改核心代码:

3.1 创建Widget类

指定渲染位置为奖励积分设置页的后置区块:

public class RewardPointsSettingsWidget : IWidgetPlugin
{
    private readonly ISettingService _settingService;
    private readonly ICustomerRoleService _customerRoleService;

    public RewardPointsSettingsWidget(ISettingService settingService, ICustomerRoleService customerRoleService)
    {
        _settingService = settingService;
        _customerRoleService = customerRoleService;
    }

    public async Task<IViewComponentResult> GetViewComponentAsync(string widgetZone, object additionalData)
    {
        var settings = await _settingService.LoadSettingAsync<RewardPointsDisableByRoleSettings>();
        var roles = await _customerRoleService.GetAllCustomerRolesAsync(true);
        
        var model = new RewardPointsDisableByRoleSettingsModel
        {
            DisabledCustomerRoleIds = settings.DisabledCustomerRoleIds,
            AvailableCustomerRoles = roles.Select(r => new SelectListItem 
            { 
                Text = r.Name, 
                Value = r.Id.ToString() 
            }).ToList()
        };
        return View("~/Plugins/Nop.Plugin.RewardPoints.DisableByCustomerRole/Views/Admin/RewardPointsSettingsWidget.cshtml", model);
    }

    public bool HideInWidgetList => true;
    public Task<IList<string>> GetWidgetZonesAsync() 
        => Task.FromResult<IList<string>>(new List<string> { "Admin.RewardPointsSettings.Blocks.After" });
}

3.2 创建视图文件

RewardPointsSettingsWidget.cshtml,复用Nop自带的多角色选择控件:

<div class="panel panel-default">
    <div class="panel-heading">
        <h4 class="panel-title">禁用奖励积分的客户角色</h4>
    </div>
    <div class="panel-body">
        <div class="form-group">
            <div class="col-md-3">
                <label asp-for="DisabledCustomerRoleIds">选择角色</label>
            </div>
            <div class="col-md-9">
                <select asp-for="DisabledCustomerRoleIds" asp-items="Model.AvailableCustomerRoles" 
                        class="form-control" multiple="multiple"></select>
            </div>
        </div>
    </div>
</div>

3.3 创建视图模型

public class RewardPointsDisableByRoleSettingsModel : BaseNopModel
{
    public int[] DisabledCustomerRoleIds { get; set; }
    [UIHint("CustomerRole")]
    public List<SelectListItem> AvailableCustomerRoles { get; set; } = new();
}

4. 拦截奖励积分计算逻辑

通过覆盖服务的方式,对指定角色用户禁用积分计算:

4.1 自定义奖励积分服务

public class CustomRewardPointsService : RewardPointsService
{
    private readonly ISettingService _settingService;
    private readonly ICustomerService _customerService;

    public CustomRewardPointsService(ISettingService settingService, ICustomerService customerService,
        IRepository<RewardPointsHistory> rewardPointsHistoryRepository,
        IRepository<Customer> customerRepository,
        IWorkContext workContext,
        ILocalizationService localizationService,
        IStoreContext storeContext,
        ISettingService baseSettingService)
        : base(rewardPointsHistoryRepository, customerRepository, workContext, localizationService, storeContext, baseSettingService)
    {
        _settingService = settingService;
        _customerService = customerService;
    }

    public override async Task<int> CalculateRewardPointsAsync(Customer customer, decimal amount, int storeId)
    {
        var settings = await _settingService.LoadSettingAsync<RewardPointsDisableByRoleSettings>();
        var customerRoles = await _customerService.GetCustomerRolesAsync(customer);
        
        // 若用户属于禁用角色,返回0积分
        if (customerRoles.Any(r => settings.DisabledCustomerRoleIds.Contains(r.Id)))
            return 0;
        
        return await base.CalculateRewardPointsAsync(customer, amount, storeId);
    }
}

4.2 替换核心服务

在主插件类的PostInitializeAsync方法中替换默认服务:

public override async Task PostInitializeAsync()
{
    _serviceProvider.ReplaceService<IRewardPointsService, CustomRewardPointsService>(ServiceLifetime.Scoped);
    await base.PostInitializeAsync();
}

5. 保存设置

创建表单处理器,在提交奖励积分设置时保存禁用角色配置:

public class RewardPointsSettingsFormHandler : IFormHandler
{
    private readonly ISettingService _settingService;

    public RewardPointsSettingsFormHandler(ISettingService settingService)
    {
        _settingService = settingService;
    }

    public async Task HandleFormSubmittedAsync(IFormCollection form, int storeId)
    {
        var settings = await _settingService.LoadSettingAsync<RewardPointsDisableByRoleSettings>(storeId);
        var disabledRoleIds = form["DisabledCustomerRoleIds"].ToString()
            .Split(',', StringSplitOptions.RemoveEmptyEntries)
            .Select(int.Parse)
            .ToArray();
            
        settings.DisabledCustomerRoleIds = disabledRoleIds;
        await _settingService.SaveSettingAsync(settings, storeId);
    }

    public bool CanHandleForm(string formName) => formName == "RewardPointsSettings";
}

在插件初始化时注册该处理器:

public override async Task InitializeAsync()
{
    _serviceProvider.AddScoped<IFormHandler, RewardPointsSettingsFormHandler>();
    await base.InitializeAsync();
}

6. 测试验证

  1. 编译插件,复制到Nopcommerce的Plugins目录
  2. 后台安装插件,进入配置→奖励积分设置,即可看到新增的角色选择区域
  3. 创建测试用户并分配到禁用角色,下单验证是否不会获得奖励积分

内容的提问来源于stack exchange,提问作者CK Vignesh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 05:50:35