package com.hzs.common.core.aspect;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ReflectUtil;
import com.hzs.common.core.annotation.ColumnAuthority;
import com.hzs.common.core.context.SecurityContextHolder;
import com.hzs.common.core.enums.EYesNo;
import com.hzs.common.core.web.domain.AjaxResult;
import com.hzs.common.core.web.page.TableDataInfo;
import com.hzs.common.domain.system.sys.SysRoleMenuColumn;
import com.hzs.system.sys.IMenuColumnServiceApi;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.*;

/**
 * @Description: 列权限控制返回处理
 * @Author: jiang chao
 * @Time: 2023/7/28 10:38
 * @Classname: ColumnAspectAdvice
 * @PackageName: com.hzs.common.core.aspect
 */
@Slf4j
@Order(10)
@Aspect
@Component
public class ColumnAuthorityAspectAdvice {

    @DubboReference
    private IMenuColumnServiceApi iMenuColumnServiceApi;

    /**
     * 定义切点Pointcut
     */
    @Pointcut("@annotation(com.hzs.common.core.annotation.ColumnAuthority)")
    public void columnAuthorityPointCut() {
    }

    /**
     * 执行返回时进行处理
     *
     * @param joinPoint
     * @param columnAuthority 权限列注解
     * @param returnValue     返回数据
     * @return
     */
    @AfterReturning(value = "columnAuthorityPointCut() && @annotation(columnAuthority)", returning = "returnValue")
    public Object columnAuthorityPointAfter(JoinPoint joinPoint, ColumnAuthority columnAuthority, Object returnValue) {
        // 返回值不为空，则进行处理
        if (null != returnValue) {
            long startTime = System.currentTimeMillis();

            // 当前登录用户所能查看的权限列表
            // 列权限map（key: 列名，value: 是否显示（0=显示））
            Map<String, Integer> columnMap = iMenuColumnServiceApi.queryMenuColumn(columnAuthority.perms(), SecurityContextHolder.getUserId()).getData();
            // 需要处理字段权限
            if (CollectionUtil.isNotEmpty(columnMap)) {
                if (returnValue instanceof AjaxResult) {
                    // 返回为统一 AjaxResult
                    AjaxResult ajaxResult = (AjaxResult) returnValue;
                    // 返回内容
                    Object ajaxResultObj = ajaxResult.get(AjaxResult.DATA_TAG);

                    if (ajaxResultObj instanceof Collection) {
                        // 返回内容为集合处理
                        Collection dataList = (Collection) ajaxResultObj;

                        this.handleFieldValue(dataList, columnMap);
                    }
                } else if (returnValue instanceof TableDataInfo) {
                    // 返回为分页数据
                    TableDataInfo tableDataInfo = (TableDataInfo) returnValue;
                    Collection dataList = tableDataInfo.getRows();

                    this.handleFieldValue(dataList, columnMap);
                }

                log.info("列权限控制截器处理结束，毫秒用时 {}", System.currentTimeMillis() - startTime);
            }
        }
        return returnValue;
    }

    /**
     * 集合内字段处理
     *
     * @param dataList  集合数据列表
     * @param columnMap 列权限map（key: 列名，value: 是否显示（0=显示））
     */
    private void handleFieldValue(Collection dataList, Map<String, Integer> columnMap) {
        // 数据集合不为空则进行处理
        if (CollectionUtil.isNotEmpty(dataList)) {
            for (Object obj : dataList) {
                // 内容不为空，则进行处理
                if (null != obj) {
                    // 获取内容对应实体
                    Class dataClass = obj.getClass();
                    // 获取实体字段
                    Field[] fields = ReflectUtil.getFields(dataClass);

                    for (Field field : fields) {
                        Integer showFlag = columnMap.get(field.getName());
                        if (null != showFlag && EYesNo.NO.getIntValue() == showFlag) {
                            // 存在控制标记并且为不显示，则清除返回字段内容
                            ReflectUtil.setFieldValue(obj, field, null);
                        }
                    }
                }
            }
        }
    }

}
