Oinone登录扩展:对接SSO(适应于4.7.8之前的版本)

适配版本

4.7.8之前的版本

概述

在企业内部,对于已有一套完整的登录系统(SSO)的情况下,通常会要求把所有的系统都对接到SSO中;本文主要讲解用Oinone开发的项目对接SSO的具体实现。

对接步骤

1、项目自定义实现UserCookieLogin,可参考示例说明:

pro.shushi.pamirs.user.api.login.UserCookieLoginFree

2、对接SSO示例

对接流程说明:

  • 1)【必须】从请求头Header或者Query中获取到token;
  • 2)【必须】去SSO服务端验证token的有效性;
  • 3)【可选】根据token去服务端获取用户信息;如果token可以直接反解析出用户信息,则该步骤忽略;
  • 4)【可选】根据实际情况用户信息是否进行DB的存储;
  • 5)【必须】验证token有效后,生成Session和Cookie(即token换cookie); 注意超时时间需要 <= SSO服务端token失效时间。
package pro.shushi.pamirs.demo.core.sso;

import com.alibaba.fastjson.JSON;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import pro.shushi.pamirs.demo.core.sso.constant.HttpConstant;
import pro.shushi.pamirs.demo.core.sso.constant.SSOConstant;
import pro.shushi.pamirs.demo.core.sso.constant.SessionUserTypeEnum;
import pro.shushi.pamirs.demo.core.sso.model.ApiCommonTransient;
import pro.shushi.pamirs.demo.core.sso.utils.AuthenticateUtils;
import pro.shushi.pamirs.demo.core.sso.model.PermissionInfoResp;
import pro.shushi.pamirs.meta.annotation.fun.extern.Slf4j;
import pro.shushi.pamirs.meta.api.dto.model.PamirsUserDTO;
import pro.shushi.pamirs.meta.api.dto.protocol.PamirsRequestVariables;
import pro.shushi.pamirs.meta.api.session.PamirsSession;
import pro.shushi.pamirs.meta.common.exception.PamirsException;
import pro.shushi.pamirs.meta.common.spring.BeanDefinitionUtils;
import pro.shushi.pamirs.meta.common.util.UUIDUtil;
import pro.shushi.pamirs.resource.api.enmu.UserSignUpType;
import pro.shushi.pamirs.user.api.cache.UserCache;
import pro.shushi.pamirs.user.api.constants.UserConstant;
import pro.shushi.pamirs.user.api.enmu.UserExpEnumerate;
import pro.shushi.pamirs.user.api.enmu.UserLoginTypeEnum;
import pro.shushi.pamirs.user.api.login.IUserLoginChecker;
import pro.shushi.pamirs.user.api.login.UserCookieLogin;
import pro.shushi.pamirs.user.api.login.UserCookieLoginSimple;
import pro.shushi.pamirs.user.api.model.PamirsUser;
import pro.shushi.pamirs.user.api.model.tmodel.PamirsUserTransient;
import pro.shushi.pamirs.user.api.service.UserService;
import pro.shushi.pamirs.user.api.utils.CookieUtil;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.concurrent.TimeUnit;

/**
 *
 * @author wangxian
 *
 *  完全自定义login的过程
 *  需要实现登陆部分login 以及拦截部分fetchUserIdByReq
 *  如果fetchUserIdByReq返回值为null的时候 将会被拦截
 */
@Slf4j
@Order(0)
@Component
public class DemoUserSSOCookieLogin extends UserCookieLogin<PamirsUser>  {

    //刷新令牌
    private static String REFRESH_TOKEN = "refreshToken";
    //系统id
    private static String CLIENT_ID = "client-id";
    //访问令牌
    private static String AUTHORIZATION = "Authorization";

    private IUserLoginChecker checker;

    @Autowired
    private UserService userService;
    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Override
    public String createSessionId(HttpServletRequest request, PamirsUser idModel) {
        return UUIDUtil.getUUIDNumberString();
    }

    @Override
    public String type() {
        return UserLoginTypeEnum.COOKIE.value();
    }

    @Override
    public PamirsUser resolveAndVerification(PamirsUserTransient user) {
        if (checker == null) {
            checker = BeanDefinitionUtils.getBean(IUserLoginChecker.class);
        }
        return checker.check4login(user);
    }

    /**
     * 重写登录拦截功能
     * 该函数主要作用,通过三方权限校验.
     * @param request
     * @return
     */
    @Override
    public PamirsUserDTO fetchUserIdByReq(HttpServletRequest request) {
        PamirsRequestVariables pamirsRequestVariables =  PamirsSession.getRequestVariables();
        log.debug("==cookie== {}", JSON.toJSONString(request.getCookies()));
        log.debug("==headNames== {}", JSON.toJSONString(request.getHeaderNames()));
        log.debug("==params=={}",JSON.toJSONString(request.getContextPath()));
        String sessionId = CookieUtil.getValue(request, "pamirs_uc_session_id");
        PamirsUserDTO pamirsUserDTO = UserCache.get(sessionId);
        if (pamirsUserDTO ==null) {
            //H5-企业微信登录,其他SSO登录。获取标识。根据实际情况修改和适配
            String accessToken = request.getParameter("accessToken");
            ApiCommonTransient permissionInfo = AuthenticateUtils.getPermissionInfo(accessToken);
            // *******登录成功的条件判断,各SSO根据情况自行修改********
            if(HttpConstant.SUCCESS.equals(permissionInfo.getCode())) {
                // SSO用户换 Oinone 的用户
                PamirsUser pamirsUser = setUserInfoToCookiesAndSetUserIdToCache(permissionInfo,accessToken);
                return new PamirsUserDTO().setUserId(pamirsUser.getId()).setPhone(pamirsUser.getPhone()).setUserCode(
                        pamirsUser.getCode()).setLogin(pamirsUser.getLogin()).setEmail(pamirsUser.getEmail()).setUserName(pamirsUser.getName());
            } else {
                //用户未登录,并且token未获取到用户null(根据实际情况修改)
                return null;
            }
        } else {
            return fetchUserIdByReq4Pamirs(request);
        }
    }

    /**
     * 开放一个用户登录setCookies函数 供登录和跳转校验
     * @param permissionInfo
     * @param accessToken
     * @return
     */
    public PamirsUser setUserInfoToCookiesAndSetUserIdToCache(ApiCommonTransient permissionInfo, String accessToken) {
        String sessionId;
        PermissionInfoResp ssoResponseTransient = JSON.parseObject(permissionInfo.getData().toString(), PermissionInfoResp.class);
        // *********示例:SSO跟业务系统中用户ID关联;其他关联条件请根据数据情况处理*******
        PamirsUser pamirsUser = userService.queryById(ssoResponseTransient.getUserInfo().getId());
        if (pamirsUser == null) {
            // ********SSO中有用户数据,Oinone的系统中没有需要怎么处理************/
            // ********这里仅仅是一个示例,在上述情况下创建用户,请根据实际情况处理,请根据实际情况处理*****/
            pamirsUser = createOrUpdatePamirsUser(ssoResponseTransient);
        }
        HttpServletRequest httpServletRequest = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        HttpServletResponse httpServletResponse = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
        UserCookieLoginSimple userCookieLoginSimple = new UserCookieLoginSimple();

        sessionId = userCookieLoginSimple.createSessionId(httpServletRequest, null);
        String cacheKey = userCookieLoginSimple.parseSessionId(sessionId);
        UserCache.putCache(cacheKey, userCookieLoginSimple.coverToUserDTO(pamirsUser));
        try {
            CookieUtil.set(httpServletResponse, UserConstant.USER_SESSION_ID, sessionId);
        } catch (Exception e) {
            log.error("SSO Login Cookie Set Err", e);
        }

        //set accessToken to cookies
        String userCodeCacheKey = SSOConstant.USER_REDIS_CACHE + pamirsUser.getId();
        redisTemplate.opsForValue().set(userCodeCacheKey,accessToken,3600, TimeUnit.SECONDS);
        return pamirsUser;
    }

    // ********这里仅仅是一个示例,在上述情况下创建用户,请根据实际情况处理,请根据实际情况处理*****/
    private PamirsUser createOrUpdatePamirsUser(PermissionInfoResp ssoResponseTransient) {
        PamirsUser pamirsUser;
        pamirsUser = new PamirsUser();
        pamirsUser.unsetEmail();
        pamirsUser.unsetPhone() ;
        pamirsUser.setId(ssoResponseTransient.getUserInfo().getId());
        pamirsUser.setUserType(SessionUserTypeEnum.COMPANY_ADMIN.displayName());
        pamirsUser.setSignUpType(UserSignUpType.BACKSTAGE);
        pamirsUser.setName(ssoResponseTransient.getUserInfo().getUsername());
        pamirsUser.setLogin(ssoResponseTransient.getUserInfo().getUsername());
        pamirsUser.setNickname(ssoResponseTransient.getUserInfo().getNickname());
        pamirsUser.setRealname(ssoResponseTransient.getUserInfo().getNickname());
        pamirsUser.setId(ssoResponseTransient.getUserInfo().getId());
        userService.createOrUpdate(pamirsUser);
        return pamirsUser;
    }

    /**
     * 原始用户登陆后函数调用
     * @param request
     * @return
     */
    private PamirsUserDTO fetchUserIdByReq4Pamirs(HttpServletRequest request) {
        PamirsUserDTO pamirsUserDTO = super.fetchUserIdByReq(request);
        if (pamirsUserDTO == null || pamirsUserDTO.getUserId() == null) {
            return pamirsUserDTO;
        }
        PamirsUser user = new PamirsUser().setId(pamirsUserDTO.getUserId()).queryById();
        if (user != null && !Boolean.TRUE.equals(user.getActive())) {
            //清理下登录的cookie
            String sessionId = CookieUtil.getValue(request, UserConstant.USER_SESSION_ID);
            if (!StringUtils.isBlank(sessionId)) {
                HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
                UserCache.logout(request, response, sessionId);
            }
            log.error("{}当前用户是{},{}", UserExpEnumerate.USER_CAN_NOT_ACTIVE_ERROR, pamirsUserDTO.getUserId(), pamirsUserDTO.getLogin());
            throw PamirsException.construct(UserExpEnumerate.USER_CAN_NOT_ACTIVE_ERROR).errThrow();
        }

        return pamirsUserDTO;
    }

}

3、通常自定义登录的时候需要获取HttpRequest和HttpResponse,从HttpRequest获取请求头或者请求参数,获取方式如下:

// HttpServletRequest获取
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
// HttpServletResponse获取
HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();

对接SSO示例代码包

示例代码包点击可以进行下载:
SS0对接Demo示例

Oinone社区 作者:望闲原创文章,如若转载,请注明出处:https://doc.oinone.top/backend/4747.html

访问Oinone官网:https://www.oinone.top获取数式Oinone低代码应用平台体验

(0)
望闲的头像望闲数式管理员
上一篇 2023年11月24日 am9:44
下一篇 2023年11月24日 pm4:51

相关推荐

  • 流程设计流程结束通知SPI接口

    1.实现SPI接口 import pro.shushi.pamirs.meta.common.spi.SPI; import pro.shushi.pamirs.meta.common.spi.factory.SpringServiceLoaderFactory; import pro.shushi.pamirs.workflow.app.api.entity.WorkflowContext; import pro.shushi.pamirs.workflow.app.api.model.WorkflowInstance; @SPI(factory = SpringServiceLoaderFactory.class) public interface WorkflowEndNoticeApi { void execute(WorkflowContext context, WorkflowInstance instance); } 自定义通知逻辑 /** * 自定义扩展流程结束时扩展点 */ @Order(999) @Component @SPI.Service public class MyWorkflowEndNoticeApi implements WorkflowEndNoticeApi { @Override public void execute(WorkflowContext context, WorkflowInstance instance) { Long dataBizId = instance.getDataBizId(); //todo自定义逻辑 } }

    2023年12月26日
    1.3K00
  • 集成开放-开放接口如何鉴权加密

    使用前提 已经阅读过文档【oinone 7天从入门到精通】的6.2章节-集成平台 已经依赖了内置模块集成平台eip boot启动工程pom.xml新增jar依赖 <dependency> <groupId>pro.shushi.pamirs.core</groupId> <artifactId>pamirs-eip2-core</artifactId> </dependency> <dependency> <groupId>pro.shushi.pamirs.core</groupId> <artifactId>pamirs-eip2-view</artifactId> </dependency> 配置文件application.yml新增启动依赖模块 pamirs: boot: modules: – eip eip: open-api: enabled: true route: # 开放接口访问IP,开放外网可以配置为0.0.0.0 host: 127.0.0.1 # 开放接口访问端口 port: 8094 # 认证Token加密的AES密钥 aes-key: NxDZUddmvdu3QQpd5jIww2skNx6U0w0uOAXj3NUCLu8= 一、新增开放接口示例代码 开放接口类定义 package pro.shushi.pamirs.demo.api.open; import pro.shushi.pamirs.meta.annotation.Fun; import pro.shushi.pamirs.meta.annotation.Function; @Fun(TestOpenApiModelService.FUN_NAMESPACE) public interface TestOpenApiModelService { String FUN_NAMESPACE = "demo.open.TestOpenApiModelService"; @Function TestOpenApiModel queryById(Long id); } 开放接口实现类 package pro.shushi.pamirs.demo.core.open; import org.apache.camel.ExtendedExchange; import org.springframework.stereotype.Component; import pro.shushi.pamirs.core.common.SuperMap; import pro.shushi.pamirs.demo.api.open.TestEipConfig; import pro.shushi.pamirs.demo.api.open.TestOpenApiModel; import pro.shushi.pamirs.demo.api.open.TestOpenApiModelService; import pro.shushi.pamirs.demo.api.open.TestOpenApiResponse; import pro.shushi.pamirs.eip.api.IEipContext; import pro.shushi.pamirs.eip.api.annotation.Open; import pro.shushi.pamirs.eip.api.constant.EipFunctionConstant; import pro.shushi.pamirs.eip.api.enmu.EipExpEnumerate; import pro.shushi.pamirs.eip.api.entity.openapi.OpenEipResult; import pro.shushi.pamirs.meta.annotation.Fun; import pro.shushi.pamirs.meta.annotation.Function; import pro.shushi.pamirs.meta.common.exception.PamirsException; import java.util.Optional; @Fun(TestOpenApiModelService.FUN_NAMESPACE) @Component public class TestOpenApiModelServiceImpl implements TestOpenApiModelService { @Override @Function public TestOpenApiModel queryById(Long id) { return new TestOpenApiModel().queryById(id); } @Function @Open @Open.Advanced( authenticationProcessorFun = EipFunctionConstant.DEFAULT_AUTHENTICATION_PROCESSOR_FUN, authenticationProcessorNamespace = EipFunctionConstant.FUNCTION_NAMESPACE ) public OpenEipResult<TestOpenApiResponse> queryById4Open(IEipContext<SuperMap> context , ExtendedExchange exchange) { String id = Optional.ofNullable(String.valueOf(context.getInterfaceContext().getIteration("id"))).orElse(""); TestOpenApiModel temp = queryById(Long.valueOf(id)); TestOpenApiResponse response = new TestOpenApiResponse(); if(temp != null ) { response.setAge(temp.getAge()); response.setId(temp.getId()); response.setName(temp.getName()); }else{ response.setAge(1); response.setId(1L); response.setName("oinone eip test"); } OpenEipResult<TestOpenApiResponse> result = new OpenEipResult<TestOpenApiResponse>(response); return result; } @Function @Open(config = TestEipConfig.class,path = "error") @Open.Advanced( httpMethod = "post", authenticationProcessorFun = EipFunctionConstant.DEFAULT_AUTHENTICATION_PROCESSOR_FUN, authenticationProcessorNamespace…

    2024年7月25日
    1.3K00
  • 后端代码规范

    前言 虽然oinone框架减少了很多的代码,但是低代码部分的代码质量也需要高度关注,不管是写的代码bug多,或者说被吐槽代码不行,还是说写的代码经常被重构,核心点还是没有代码规范的意识和技巧,下面摘录了一些常见的规范要求,去提高后端的代码质量,代码质量提高后,自然效率也会提升。 常见代码规范 **1、规范命名** 命名是写代码中最频繁的操作,比如类、属性、方法、参数等。好的名字应当能遵循以下几点: **见名知意** 比如需要定义一个变量需要来计数 int i = 0; 名称 i 没有任何的实际意义,没有体现出数量的意思,所以我们应当指明数量的名称 int count = 0; **能够读的出来** 如下代码: private String sfzh; private String dhhm; 这些变量的名称,根本读不出来,更别说实际意义了。 所以我们可以使用正确的可以读出来的英文来命名 private String idCardNo; private String phone; **2、规范代码格式** 好的代码格式能够让人感觉看起来代码更加舒适。 好的代码格式应当遵守以下几点: 合适的空格 代码对齐,比如大括号要对齐 及时换行,一行不要写太多代码 好在现在开发工具支持一键格式化,可以帮助美化代码格式,大家统一使用idea的规范即可。 **3、写好代码注释** 在《代码整洁之道》这本书中作者提到了一个观点,注释的恰当用法是用来弥补我们在用代码表达意图时的失败。换句话说,当无法通过读代码来了解代码所表达的意思的时候,就需要用注释来说明。 书的作者之所以这么说,是因为作者觉得随着时间的推移,代码可能会变动,如果不及时更新注释,那么注释就容易产生误导,偏离代码的实际意义。而不及时更新注释的原因是,程序员不喜欢写注释。😒 但是这不意味着可以不写注释,当通过代码如果无法表达意思的时候,就需要注释,比如如下代码: for (Integer id : ids) { if (id == 0) { continue; } //做其他事 } 为什么 id == 0 需要跳过,代码是无法看出来了,就需要注释了。 好的注释应当满足一下几点: 解释代码的意图,说明为什么这么写,用来做什么 对参数和返回值注释,入参代表什么,出参代表什么 有警示作用,比如说入参不能为空,或者代码是不是有坑 当代码还未完成时可以使用 todo 注释来标记 代码review发现漏洞时 可以使用 fixme 注释来标记 **4、try catch 内部代码抽成一个方法** try catch代码有时会干扰我们阅读核心的代码逻辑,这时就可以把try catch内部主逻辑抽离成一个单独的方法 如下图是Eureka服务端源码中服务下线的实现中的一段代码 整个方法非常长,try中代码是真正的服务下线的代码实现,finally可以保证读锁最终一定可以释放。 所以这段代码其实就可以对核心的逻辑进行抽取。 protected boolean internalCancel(String appName, String id, boolean isReplication) { try { read.lock(); doInternalCancel(appName, id, isReplication); } finally { read.unlock(); } // 剩余代码 } private boolean doInternalCancel(String appName, String id, boolean isReplication) { //真正处理下线的逻辑 } **5、方法别太长** 方法别太长就是字面的意思。一旦代码太长,给人的第一眼感觉就很复杂,让人不想读下去; 同时方法太长的代码可能读起来容易让人摸不着头脑,不知道哪一些代码是同一个业务的功能。 比如代码中有那种2000+行大类,各种if else判断,光理清代码思路就需要用很久时间。🤷🏻‍♀️ 所以一旦方法过长,可以尝试将相同业务功能的代码单独抽取一个方法,最后在主方法中调用即可。 **6、抽取重复代码** 当一份代码重复出现在程序的多处地方,就会造成程序又臭又长,当这份代码的结构要修改时,每一处出现这份代码的地方都得修改,导致程序的扩展性很差。 所以一般遇到这种情况,可以抽取成一个工具类,还可以抽成一个公共的父类。 **7、多用return** 在有时我们平时写代码的情况可能会出现if条件套if的情况,当if条件过多的时候可能会出现如下情况: if (条件1) { if (条件2) { if (条件3) { if (条件4) { if (条件5) { System.out.println("11111"); } } } } } 面对这种情况,可以换种思路,使用return来优化 if (!条件1) { return; } if (!条件2) { return; } if (!条件3) { return; } if (!条件4) { return; } if (!条件5) { return; } System.out.println("11111"); 这样优化就感觉看起来更加直观 **8、if条件表达式不要太复杂**…

    2024年12月11日
    2.7K00
  • 如何通过自定义支持excel导出的动态表头

    介绍 本文需要阅读过前置文档如何自定义Excel导出功能,动态表头的功能在前置文档的基础上做的进一步扩展,本文未提到的部分都参考这个前置文档。 在日常的业务开发中,我们在导出的场景会遇到需要设置动态表头的场景,比如统计商品在最近1个月的销量,固定表头列为商品的名称等基础信息,动态表头列为最近一个月的日期,在导出的时候设置每个日期的销量,本文将通过此业务场景提供示例代码。 1.自定义导出任务模型 package pro.shushi.pamirs.demo.api.model; import pro.shushi.pamirs.file.api.model.ExcelExportTask; import pro.shushi.pamirs.meta.annotation.Model; @Model.model(DemoItemDynamicExcelExportTask.MODEL_MODEL) @Model(displayName = "商品-Excel动态表头导出任务") public class DemoItemDynamicExcelExportTask extends ExcelExportTask { public static final String MODEL_MODEL = "demo.DemoItemDynamicExcelExportTask"; } 2.自定义导出任务处理数据的扩展点 package pro.shushi.pamirs.demo.core.excel.exportdemo.extPoint; import org.springframework.stereotype.Component; import pro.shushi.pamirs.core.common.FetchUtil; import pro.shushi.pamirs.core.common.cache.MemoryIterableSearchCache; import pro.shushi.pamirs.demo.api.model.DemoItem; import pro.shushi.pamirs.file.api.config.FileConstant; import pro.shushi.pamirs.file.api.context.ExcelDefinitionContext; import pro.shushi.pamirs.file.api.enmu.ExcelTemplateTypeEnum; import pro.shushi.pamirs.file.api.entity.EasyExcelCellDefinition; import pro.shushi.pamirs.file.api.extpoint.impl.ExcelExportSameQueryPageTemplate; import pro.shushi.pamirs.file.api.model.ExcelExportTask; import pro.shushi.pamirs.file.api.model.ExcelWorkbookDefinition; import pro.shushi.pamirs.file.api.util.ExcelFixedHeadHelper; import pro.shushi.pamirs.file.api.util.ExcelHelper; import pro.shushi.pamirs.file.api.util.ExcelTemplateInit; import pro.shushi.pamirs.framework.common.entry.TreeNode; import pro.shushi.pamirs.meta.annotation.ExtPoint; import pro.shushi.pamirs.meta.api.CommonApiFactory; import pro.shushi.pamirs.meta.api.core.orm.ReadApi; import pro.shushi.pamirs.meta.api.core.orm.systems.relation.RelationReadApi; import pro.shushi.pamirs.meta.api.dto.config.ModelConfig; import pro.shushi.pamirs.meta.api.dto.config.ModelFieldConfig; import pro.shushi.pamirs.meta.api.session.PamirsSession; import pro.shushi.pamirs.meta.enmu.TtypeEnum; import pro.shushi.pamirs.meta.util.FieldUtils; import java.util.*; @Component public class DemoItemDynamicExportExtPoint extends ExcelExportSameQueryPageTemplate<DemoItem> implements ExcelTemplateInit { public static final String TEMPLATE_NAME ="商品动态导出"; @Override public List<ExcelWorkbookDefinition> generator() { ExcelFixedHeadHelper excelFixedHeadHelper = ExcelHelper.fixedHeader(DemoItem.MODEL_MODEL,TEMPLATE_NAME) .createBlock(TEMPLATE_NAME, DemoItem.MODEL_MODEL) .setType(ExcelTemplateTypeEnum.EXPORT); return Collections.singletonList(excelFixedHeadHelper.build()); } public static void buildHeader(ExcelFixedHeadHelper excelFixedHeadHelper) { excelFixedHeadHelper.addColumn("name","名称") .addColumn("cateName","类目") .addColumn("searchFrom","搜索来源") .addColumn("description","描述") .addColumn("itemPrice","单价") .addColumn("inventoryQuantity","库存"); } @Override @ExtPoint.Implement(expression = "context.model == \"" + DemoItem.MODEL_MODEL+"\" && context.name == \"" +TEMPLATE_NAME+"\"" ) public List<Object> fetchExportData(ExcelExportTask exportTask, ExcelDefinitionContext context) { List<Object> result = super.fetchExportData(exportTask,context); Object block = result.get(0); if (block instanceof ArrayList) { ((List<Object>) block).forEach(o -> { if (o instanceof DemoItem) { DemoItem item = (DemoItem) o; // TODO 设置动态表头部分字段的值 item.get_d().put("2024-09-10", "1111"); item.get_d().put("2024-09-11", "2222"); }…

    2024年9月11日
    3.7K00
  • 多模型联表查询

    多模型联表查询 多对一或者一对一关联关系,通过关联模型的字段查询数据 模型结构定义 模型A @Model(displayName = "A") @Model.model(A.MODEL_MODEL) public class A extends IdModel { public final static String MODEL_MODEL = "test.A"; @Field(displayName = "b") @Field.many2one @Field.Relation(relationFields = {"bId"}, referenceFields = {"id"}) private B b; @Field(displayName = "bId") @Field.Integer private Long bId; @Field(displayName = "B审批状态") @Field.Enum @Field.Related(related = {"b", "approvalEnum"}) private ApprovalEnum approvalEnum; } 模型B @Model(displayName = "B") @Model.model(B.MODEL_MODEL) public class B extends IdModel { public final static String MODEL_MODEL = "test.B"; @Field(displayName = "审批状态") @Field.Enum private ApprovalEnum approvalEnum; } 页面设计 在界面设计器中, 设计相对应的表格页面。 A模型related字段拖到搜索栏中。 发布页面 自定义Hook import cz.jirutka.rsql.parser.ast.RSQLOperators; import org.apache.commons.lang3.ArrayUtils; import org.springframework.stereotype.Component; import pro.shushi.pamirs.framework.connectors.data.sql.AbstractWrapper; import pro.shushi.pamirs.framework.connectors.data.sql.query.QueryWrapper; import pro.shushi.pamirs.meta.annotation.Hook; import pro.shushi.pamirs.meta.api.Models; import pro.shushi.pamirs.meta.api.core.faas.HookBefore; import pro.shushi.pamirs.meta.api.core.orm.convert.ClientDataConverter; import pro.shushi.pamirs.meta.api.core.orm.template.context.ModelComputeContext; import pro.shushi.pamirs.meta.api.dto.config.ModelConfig; import pro.shushi.pamirs.meta.api.dto.config.ModelFieldConfig; import pro.shushi.pamirs.meta.api.dto.fun.Function; import pro.shushi.pamirs.meta.api.session.PamirsSession; import pro.shushi.pamirs.meta.base.D; import pro.shushi.pamirs.meta.common.spi.Spider; import pro.shushi.pamirs.meta.domain.model.ModelField; import pro.shushi.pamirs.meta.enmu.TtypeEnum; import pro.shushi.pamirs.resource.api.constants.FieldConstants; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; /** * 通用 queryData处理。 */ @Slf4j @Component public class QueryDataHook implements HookBefore { @Override @Hook(priority = 30) public Object run(Function function, Object… args) { getValueByType(args); return function; } private void getValueByType(Object… args) { if (ArrayUtils.isEmpty(args)) { return; } for (int index = 0; index < args.length &&…

    2025年1月9日
    1.6K00

Leave a Reply

登录后才能评论