UsAppPrintLogScopeHelper.cs 8.34 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 打印日志 / 门店报表:门店绑定校验;同一门店下已绑定账号可查看该门店全部打印记录(不按 CreatedBy 过滤)。
/// </summary>
public static class UsAppPrintLogScopeHelper
{
    /// <summary>
    /// 管理员、Company Admin,或角色码/名含 <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;
        }

        if (await TeamMemberRoleHelper.IsCompanyAdminUserAsync(db, currentUser))
        {
            return true;
        }

        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>
    /// 校验当前用户是否可访问指定门店(含 Company Admin 公司下全部门店)。
    /// </summary>
    public static async Task EnsureUserBoundToLocationAsync(
        ICurrentUser currentUser,
        ISqlSugarClient db,
        string locationId)
    {
        await EnsureUserCanAccessLocationAsync(currentUser, db, locationId);
    }

    /// <summary>
    /// 校验是否可访问指定门店:平台管理员不校验;Company Admin 可访问绑定 Company 下任意门店;其它账号须 userlocation 绑定。
    /// </summary>
    public static async Task EnsureUserCanAccessLocationAsync(
        ICurrentUser currentUser,
        ISqlSugarClient db,
        string locationId)
    {
        if (ReportsRoleHelper.IsAdminRole(currentUser))
        {
            return;
        }

        if (currentUser.Id is null)
        {
            throw new UserFriendlyException("用户未登录");
        }

        var lid = TeamMemberListScopeHelper.NormalizeScopeKey(locationId);
        if (string.IsNullOrEmpty(lid))
        {
            throw new UserFriendlyException("门店参数无效");
        }

        var accessible = await LocationScopeBindingHelper.ResolveAppAccessibleLocationIdsForUserAsync(
            db, currentUser.Id.Value, currentUser);
        if (!accessible.Contains(lid, StringComparer.OrdinalIgnoreCase))
        {
            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 => TeamMemberListScopeHelper.NormalizeScopeKey(x!))
            .Where(x => Guid.TryParse(x, out _))
            .Select(Guid.Parse)
            .Distinct()
            .ToList();
        if (guids.Count == 0)
        {
            return map;
        }

        var parameters = new List<SugarParameter>(guids.Count);
        var inClause = new List<string>(guids.Count);
        for (var i = 0; i < guids.Count; i++)
        {
            var paramName = $"@uid{i}";
            inClause.Add(paramName);
            parameters.Add(new SugarParameter(paramName, guids[i]));
        }

        var users = await db.Ado.SqlQueryAsync<OperatorUserRow>(
            $"""
             SELECT Id, UserName, Name, Nick
             FROM User
             WHERE IsDeleted = 0 AND Id IN ({string.Join(", ", inClause)})
             """,
            parameters);

        foreach (var u in users)
        {
            var display = BuildOperatorDisplayName(u.Name, u.Nick, u.UserName);
            map[TeamMemberListScopeHelper.UserKey(u.Id)] = display;
        }

        return map;
    }

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

        var key = TeamMemberListScopeHelper.NormalizeScopeKey(createdBy);
        return map.TryGetValue(key, out var name) ? name : "无";
    }

    private static string BuildOperatorDisplayName(string? name, string? nick, string? userName)
    {
        if (!string.IsNullOrWhiteSpace(name))
        {
            return name.Trim();
        }

        if (!string.IsNullOrWhiteSpace(nick))
        {
            return nick.Trim();
        }

        var user = userName?.Trim();
        return string.IsNullOrWhiteSpace(user) ? "无" : user;
    }

    private sealed class OperatorUserRow
    {
        public Guid Id { get; set; }

        public string? UserName { get; set; }

        public string? Name { get; set; }

        public string? Nick { get; set; }
    }

    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; }
    }
}