协同开发支持

协同开发概述

在使用Oinone进行业务开发中,目前开发方式为: 开发各个本地启动项目 与 设计器环境共库共redis的方式进行。

在多个开发人员同时修改一个模型,或者没有及时更新其他同学提交的代码时,存在业务模型创建的数据表字段被删除的情况,协同开发模式正式为解决这个问题而生。

版本支持

4.7.x版本 已经包含分布式支持。

使用步骤

1、业务后端boot工程引入协同开发包

<dependency>
    <groupId>pro.shushi.pamirs.distribution</groupId>
    <artifactId>pamirs-distribution-session-cd</artifactId>
</dependency>

2、yml文件配置ownSign

pamirs:
  distribution:
    session:
      allMetaRefresh: false
      # ownSign根据实际情况修改,一般为研发各自名称
      ownSign: wangxian

配置说明:
allMetaRefresh,全量刷新Redis中的元数据,绝大多数情况下都不需要配置;
1)第一次启动或者Redis的缓存被清空后,会自动进行全量。
2)配置为true表示强制进行全量,一般都不需要配置;
3)【推荐】默认增量的方式(即allMetaRefresh: false)写入redis的数据更少,相应的启动速度也更快
4)【强制】ownSign是环境隔离的设置,同一个项目组不同的开发人员之间,ownSign配置成不同的(即各自配置成各自的,达到互不干扰)

3、业务系统DB和缓存的约束
1)【强制】业务库和设计器Redis共用,包括Redis的前缀,租户和系统隔离键都需要一样(这三个值影响RedisKey的拼接)
2)【强制】base库业务系统与设计器共用;
3) 【强制】公共库即pamirs (资源-resource、用户-user、权限-auth、文件-file等)共用;
4)【强制】「业务库」数据源的别名必须一直,每个开发人员必须配置到自己的本地 或者是远程库库加一个后缀区分;

4、开发同学在各自访问设计器时,URL最后面增加;ownSign=wangxian后回车,ownSign会被保存到浏览器缓存中,后续访问其他的URL访问不需要再次输入;如果需要去掉ownSign的值,则直接把界面上的悬浮窗删掉即可。
协同开发支持
说明:访问设计URL上增加的ownSign需要与开发各自本地项目yml文件中ownSign的值相同。(每个开发人员各自用各自的ownSign)
PS:具体参数配置详见Oinone协同开发使用手册

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

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

(0)
望闲的头像望闲数式管理员
上一篇 2023年12月4日 pm8:49
下一篇 2023年12月5日 pm6:17

相关推荐

  • 首次登录修改密码和自定义密码规则等

    场景描述 在某些场景下,可能需要实现 用户首次登录强制修改密码的功能,或者存在修改平台默认密码等校验规则等需求;本文将讲解不改变平台代码的情况下,如何实现这些功能需求。 首次登录修改密码 方案概述 自定义User增加是否是第一次登录的属性,登录后执行一个扩展点。 判断是否是一次登录,如果是则返回对应的状态码,前端根据状态码重定向到修改密码的页面。修改完成则充值第一次登录的标识。 PS:首次登录的标识平台前端已默认实现 扩展PamirsUser(例如:DemoUser) /** * @author wangxian */ @Model.model(DemoUser.MODEL_MODEL) @Model(displayName = "用户", labelFields = {"nickname"}) @Model.Advanced(index = {"companyId"}) public class DemoUser extends PamirsUser { public static final String MODEL_MODEL = "demo.DemoUser"; @Field.Integer @Field.Advanced(columnDefinition = "bigint DEFAULT '0'") @Field(displayName = "公司ID", invisible = true) private Long companyId; /** * 默认true->1 */ @Field.Boolean @Field.Advanced(columnDefinition = "tinyint(1) DEFAULT '1'") @Field(displayName = "是否首次登录") private Boolean firstLogin; } 定义扩展点接口(实际项目按需要增加和删减接口的定义) import pro.shushi.pamirs.meta.annotation.Ext; import pro.shushi.pamirs.meta.annotation.ExtPoint; import pro.shushi.pamirs.user.api.model.tmodel.PamirsUserTransient; @Ext(PamirsUserTransient.class) public interface PamirsUserTransientExtPoint { @ExtPoint PamirsUserTransient loginAfter(PamirsUserTransient user); @ExtPoint PamirsUserTransient loginCustomAfter(PamirsUserTransient user); @ExtPoint PamirsUserTransient firstResetPasswordAfter(PamirsUserTransient user); @ExtPoint PamirsUserTransient firstResetPasswordBefore(PamirsUserTransient user); @ExtPoint PamirsUserTransient modifyCurrentUserPasswordAfter(PamirsUserTransient user); @ExtPoint PamirsUserTransient modifyCurrentUserPasswordBefore(PamirsUserTransient user); } 编写扩展点实现(例如:DemoUserLoginExtPoint) @Order(0) @Component @Ext(PamirsUserTransient.class) @Slf4j public class DemoUserLoginExtPoint implements PamirsUserTransientExtPoint { @Override @ExtPoint.Implement public PamirsUserTransient loginAfter(PamirsUserTransient user) { return checkFirstLogin(user); } private PamirsUserTransient checkFirstLogin(PamirsUserTransient user) { //首次登录需要修改密码 Long userId = PamirsSession.getUserId(); if (userId == null) { return user; } DemoUser companyUser = new DemoUser().queryById(userId); // 判断用户是否是第一次登录,如果是第一次登录,需要返回错误码,页面重新向登录 Boolean isFirst = companyUser.getFirstLogin(); if (isFirst) { //如果是第一次登录,返回一个标识给前端。 // 首次登录的标识平台已默认实现 user.setBroken(Boolean.TRUE); user.setErrorCode(UserExpEnumerate.USER_FIRST_LOGIN_ERROR.code()); return user; } return user; } @Override public PamirsUserTransient loginCustomAfter(PamirsUserTransient user) { return checkFirstLogin(user); } @Override…

    2024年5月25日
    5.5K00
  • 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.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.SessionUserTypeEnum; import pro.shushi.pamirs.demo.core.sso.model.ApiCommonTransient; import pro.shushi.pamirs.demo.core.sso.model.PermissionInfoResp; import pro.shushi.pamirs.demo.core.sso.utils.AuthenticateUtils; import pro.shushi.pamirs.meta.annotation.fun.extern.Slf4j; import pro.shushi.pamirs.meta.api.dto.model.PamirsUserDTO; 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.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.HttpServletResponse; /** * * @author shushi * * 完全自定义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 type() { return UserLoginTypeEnum.COOKIE.value(); } @Override public PamirsUser resolveAndVerification(PamirsUserTransient user) { if (checker == null) { checker = BeanDefinitionUtils.getBean(IUserLoginChecker.class); } return checker.check4login(user); } /** * 重写登录拦截功能 * 该函数主要作用,通过三方权限校验. * @return */ // 版本升级需要修改 @Override public PamirsUserDTO fetchUserIdByReq() { String sessionId =…

    2024年4月2日
    2.2K00
  • 项目中排除掉特定的Hook和扩展点

    总体介绍 在共库共Redis的情况下,某些场景存在需要过滤掉特定Hook和扩展点(extpoint)的情况。本文介绍排除掉的配置方法 1. Oinone如何排除特定的Hook 配置: pamirs: framework: hook: excludes: – 排除的扩展点列表 示例: pamirs: framework: hook: excludes: – pro.shushi.pamirs.timezone.hook.TimezoneHookBefore – pro.shushi.pamirs.timezone.hook.TimezoneHookAfter – pro.shushi.pamirs.timezone.hook.TimezoneSessionInitHook – pro.shushi.pamirs.translate.hook.TranslateAfterHook 2. Oinone如何排除特定的扩展点 配置 pamirs: framework: extpoint: excludes: – 排除的扩展点列表 示例: pamirs: framework: extpoint: excludes: – pro.shushi.pamirs.demo.core.extpoint.PetCatTypeExtPoint

    2024年5月13日
    1.4K00
  • Oinone请求调用链路

    Oinone请求调用链路 请求格式与简单流程 在Oinone中请求数据存储在请求体中,以GQL的方式进行表示,也就是GQL格式的请求。 当我们发送一个GQL格式的请求,后端会对GQL进行解析,确定想要执行的方法,并对这个方法执行过程中所用到的模型进行构建,最后返回响应。 请求 # 请求路径 pamirs/base http://127.0.0.1:8090/pamirs/base # 请求体内容 query{ petShopProxyBQuery{ sayHello(shop:{shopName:"cpc"}){ shopName } } } 解析 # 简单理解 query 操作类型 petShopProxyBQuery 模块名称 + Query sayHello 方法 fun sayHello() 可以传入参数,参数名为 shop shopName 需要得到的值 响应 # data中的内容 "data": { "petShopQuery": { "hello": { "shopName": "cpc" } } } 具体流程 Oinone是基于SpringBoot的,在Controller中处理请求 会接收所有以 /pamirs 开始的POST请求,/pamirs/后携带的是模块名 @RequestMapping( value = "/pamirs/{moduleName:^[a-zA-Z][a-zA-Z0-9_]+[a-zA-Z0-9]$}", method = RequestMethod.POST ) public String pamirsPost(@PathVariable("moduleName") String moduleName, @RequestBody PamirsClientRequestParam gql, HttpServletRequest request, HttpServletResponse response) { …….. } 整体脉络 第四步执行中有两大重要的步骤,一步是动态构建GQL,一步是执行请求。 动态构建GQL 请求执行

    2024年12月1日
    1.4K00
  • 模型字段之序列化方式

    本文核心是带大家全面了解oinone的序列方式,包括支持的序列化类型、注意点、如果新增客户化序列化方式以及字段默认值的反序列化。 字段序列化方式说明 序列化方式 说明 备注 JSON JSON序列化 主要用于模型相关类型字段的序列化,是@Field.serialize默认选项 DOT 点拼接集合元素 COMMA 逗号拼接集合元素 BIT 按位与,2次幂数求和 非@Field.serialize可选项列表,用于二进制枚举序列化不需要配置,由oinone自动推断 字段序列化方式举例 1、给模型PetItemDetail 增加两个字段:petItemDetails类型为List 和 tags类型为List,并设置为不同的序列化方式,petItemDetails为JSON(缺省就是JSON,可不配),tags为COMMA。2、同时设置 @Field.Advanced(columnDefinition = "varchar(1024)"),防止序列化后存储过长。 @Model.model(PetItem.MODEL_MODEL) @Model(displayName = "宠物商品",summary="宠物商品",labelFields = {"itemName"}) public class PetItem extends AbstractDemoCodeModel{ public static final String MODEL_MODEL="demo.PetItem"; @Field(displayName = "品种") @Field.many2one @Field.Relation(relationFields = {"typeId"},referenceFields = {"id"}) private PetType type; @Field(displayName = "品种类型",invisible = true) private Long typeId; @Field(displayName = "详情", serialize = Field.serialize.JSON, store = NullableBoolEnum.TRUE) @Field.Advanced(columnDefinition = "varchar(1024)") private List<PetItemDetail> petItemDetails; @Field(displayName = "商品标签",serialize = Field.serialize.COMMA,store = NullableBoolEnum.TRUE,multi = true) @Field.Advanced(columnDefinition = "varchar(1024)") private List<String> tags; } 字段序列化注意点 必须使用Field#store属性将字段存储设置为NullableBoolEnum.TRUE。 使用Field#serialize属性指定序列化方式,默认为JSON。 如把PetItemDetail设置为存储模型,须在PetItem的petItemDetails字段上使用Field.Relation#store属性将关联关系存储设置为false。不然会同时存储petItemDetails字段和对应的PetItemDetail表记录 注册自己的序列化器 注册自己的序列化器(实现pro.shushi.pamirs.meta.api.core.orm.serialize.Serializer接口), 如oinone的DOT的序列化方式,用type()方法返回值做匹配,serialize和deserialize分别对应序列化和反序列化方法。 package pro.shushi.pamirs.framework.compute.serialize; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; import pro.shushi.pamirs.meta.annotation.fun.extern.Slf4j; import pro.shushi.pamirs.meta.api.core.orm.serialize.Serializer; import pro.shushi.pamirs.meta.common.constants.CharacterConstants; import pro.shushi.pamirs.meta.enmu.SerializeEnum; import pro.shushi.pamirs.meta.util.TypeUtils; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * 点表达式序列生成处理器实现 * @author shushi@shushi.pro * @version 1.0.0 */ @SuppressWarnings("rawtypes") @Slf4j @Component public class DotSerializeProcessor implements Serializer<Object, String> { @Override public String serialize(String ltype, Object value) { if (null == value) { return null; } if (List.class.isAssignableFrom(value.getClass())) { return StringUtils.join((List) value, CharacterConstants.SEPARATOR_DOT); } else { return StringUtils.join(Collections.singletonList(value), CharacterConstants.SEPARATOR_DOT); } } @SuppressWarnings("unchecked") @Override public Object deserialize(String ltype, String ltypeT, String value,…

    2024年5月24日
    1.9K00

Leave a Reply

登录后才能评论