UsAppPrintLogScopeHelper.cs 6.62 KB
using FoodLabeling.Application.Services.DbModels;
using SqlSugar;
using Volo.Abp;
using Volo.Abp.Users;
using Yi.Framework.Rbac.Domain.Entities;
using Yi.Framework.SqlSugarCore.Abstractions;

namespace FoodLabeling.Application.Helpers;

/// <summary>
/// App 打印日志 / 门店报表:门店绑定校验;管理员或 Partner 角色可查看当前门店全部打印记录。
/// </summary>
public static class UsAppPrintLogScopeHelper
{
    /// <summary>
    /// 管理员(<see cref="ReportsRoleHelper.IsAdminRole"/>)或角色码/名含 <c>partner</c>(忽略大小写,如 Partner Admin)。
    /// </summary>
    public static async Task<bool> CanViewAllPrintsAtLocationAsync(
        ICurrentUser currentUser,
        ISqlSugarClient db)
    {
        if (ReportsRoleHelper.IsAdminRole(currentUser))
        {
            return true;
        }

        if (currentUser.Id is null)
        {
            return false;
        }

        var roleRows = await db.Ado.SqlQueryAsync<UserRoleRow>(
            @"SELECT r.RoleCode, r.RoleName
              FROM UserRole ur
              INNER JOIN Role r ON ur.RoleId = r.Id
              WHERE ur.UserId = @UserId AND r.IsDeleted = 0 AND r.State = 1",
            new { UserId = currentUser.Id.Value });

        return roleRows.Any(r =>
            ContainsPartnerKeyword(r.RoleCode) || ContainsPartnerKeyword(r.RoleName));
    }

    /// <summary>
    /// 校验当前用户是否绑定指定门店。
    /// </summary>
    public static async Task EnsureUserBoundToLocationAsync(
        ICurrentUser currentUser,
        ISqlSugarClient db,
        string locationId)
    {
        if (currentUser.Id is null)
        {
            throw new UserFriendlyException("用户未登录");
        }

        var lid = locationId.Trim();
        var userIdStr = currentUser.Id.Value.ToString();
        var bound = await db.Queryable<UserLocationDbEntity>()
            .AnyAsync(x => !x.IsDeleted && x.UserId == userIdStr && x.LocationId == lid);
        if (!bound)
        {
            throw new UserFriendlyException("当前账号未绑定该门店,无法查看");
        }
    }

    /// <summary>
    /// 构建 App 门店打印任务查询(必选门店;可选仅当前用户)。
    /// </summary>
    public static ISugarQueryable<FlLabelPrintTaskDbEntity, FlLabelDbEntity, FlProductDbEntity, FlLabelTypeDbEntity,
            FlLabelTemplateDbEntity>
        BuildLocationPrintTaskQuery(
            ISqlSugarClient db,
            string locationId,
            bool restrictToCreator,
            string currentUserIdStr)
    {
        var lid = locationId.Trim();
        return db.Queryable<FlLabelPrintTaskDbEntity>()
            .LeftJoin<FlLabelDbEntity>((t, l) => t.LabelId == l.Id)
            .LeftJoin<FlProductDbEntity>((t, l, p) => t.ProductId == p.Id)
            .LeftJoin<FlLabelTypeDbEntity>((t, l, p, lt) => t.LabelTypeId == lt.Id)
            .LeftJoin<FlLabelTemplateDbEntity>((t, l, p, lt, tpl) => t.TemplateId == tpl.Id)
            .Where((t, l, p, lt, tpl) => t.LocationId == lid)
            .WhereIF(restrictToCreator, (t, l, p, lt, tpl) => t.CreatedBy == currentUserIdStr);
    }

    public static async Task<Dictionary<string, string>> LoadOperatorNameMapAsync(
        ISqlSugarClient db,
        IEnumerable<string?> userIdStrings)
    {
        var map = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
        var guids = userIdStrings
            .Where(x => !string.IsNullOrWhiteSpace(x))
            .Select(x => x!.Trim())
            .Select(x => Guid.TryParse(x, out var g) ? g : (Guid?)null)
            .Where(x => x.HasValue)
            .Select(x => x!.Value)
            .Distinct()
            .ToList();
        if (guids.Count == 0)
        {
            return map;
        }

        var users = await db.Queryable<UserAggregateRoot>()
            .Where(u => !u.IsDeleted && guids.Contains(u.Id))
            .Select(u => new { u.Id, u.Name, u.Nick, u.UserName })
            .ToListAsync();

        foreach (var u in users)
        {
            var display = !string.IsNullOrWhiteSpace(u.Name)
                ? u.Name!.Trim()
                : !string.IsNullOrWhiteSpace(u.Nick)
                    ? u.Nick!.Trim()
                    : u.UserName?.Trim() ?? string.Empty;
            map[u.Id.ToString()] = string.IsNullOrWhiteSpace(display) ? "无" : display;
        }

        return map;
    }

    public static string ResolveOperatorName(IReadOnlyDictionary<string, string> map, string? createdBy)
    {
        if (string.IsNullOrWhiteSpace(createdBy))
        {
            return "无";
        }

        return map.TryGetValue(createdBy.Trim(), out var name) ? name : "无";
    }

    private static bool ContainsPartnerKeyword(string? value) =>
        !string.IsNullOrWhiteSpace(value) &&
        value.Trim().Contains("partner", StringComparison.OrdinalIgnoreCase);

    /// <summary>
    /// Label Report 聚合用(含分类 Join + 关键字筛选)。
    /// </summary>
    public static ISugarQueryable<FlLabelPrintTaskDbEntity, FlLabelDbEntity, FlProductDbEntity,
            FlLabelCategoryDbEntity, FlProductCategoryDbEntity, FlLabelTypeDbEntity, FlLabelTemplateDbEntity>
        BuildLocationPrintTaskReportQuery(
            ISqlSugarClient db,
            string locationId,
            bool restrictToCreator,
            string currentUserIdStr,
            string? keyword)
    {
        var lid = locationId.Trim();
        var kw = keyword?.Trim();
        return db.Queryable<FlLabelPrintTaskDbEntity>()
            .LeftJoin<FlLabelDbEntity>((t, l) => t.LabelId == l.Id)
            .LeftJoin<FlProductDbEntity>((t, l, p) => t.ProductId == p.Id)
            .LeftJoin<FlLabelCategoryDbEntity>((t, l, p, lc) => l.LabelCategoryId == lc.Id)
            .LeftJoin<FlProductCategoryDbEntity>((t, l, p, lc, pc) => p.CategoryId == pc.Id)
            .LeftJoin<FlLabelTypeDbEntity>((t, l, p, lc, pc, lt) => t.LabelTypeId == lt.Id)
            .LeftJoin<FlLabelTemplateDbEntity>((t, l, p, lc, pc, lt, tpl) => t.TemplateId == tpl.Id)
            .Where((t, l, p, lc, pc, lt, tpl) => t.LocationId == lid)
            .WhereIF(restrictToCreator, (t, l, p, lc, pc, lt, tpl) => t.CreatedBy == currentUserIdStr)
            .WhereIF(!string.IsNullOrWhiteSpace(kw),
                (t, l, p, lc, pc, lt, tpl) =>
                    (p.ProductName != null && p.ProductName.Contains(kw!)) ||
                    (lc.CategoryName != null && lc.CategoryName.Contains(kw!)) ||
                    (pc.CategoryName != null && pc.CategoryName.Contains(kw!)));
    }

    private sealed class UserRoleRow
    {
        public string? RoleCode { get; set; }

        public string? RoleName { get; set; }
    }
}