SpringCloud微服务实战——搭建企业级开发框架(五十三):微信小程序授权登录增加多租户可配置界面

  GitEgg框架集成weixin-java-miniapp工具包以实现微信小程序相关接口调用功能,weixin-java-miniapp底层支持多租户扩展。每个小程序都有唯一的appid,weixin-java-miniapp的多租户实现并不是以租户标识TenantId来区分的,而是在接口调用时,传入appid,动态切换ThreadLocal的appid来实现多租户的。并且其多个微信小程序的配置,都是在配置yml文件中的,在实际业务运营过程中,如果需要新增多租户小程序就修改配置文件显然是不合适的。
  现在我们需要结合weixin-java-miniapp的多租户实现整合到我们的框架中,使多租户可通过系统配置界面来新增多租户小程序。前面我们讲了如何集成以及如何使用weixin-java-miniapp实现微信授权登录及账号绑定等,现在只需要在原来的基础上增加数据配置存储,在服务启动时由原先的读取配置文件加载相应的微信小程序接口实例,修改为可以通过读取配置文件和读取缓存配置来生成相应的微信小程序接口实例。

一、新增微信小程序配置界面

1. 微信小程序配置数据库设计

  在数据库设计的时候,我们需要知道微信小程序授权时,哪些字段需要配置,是可选字段还是必填字段,这里我们通过weixin-java-miniapp的springboot工程配置文件可知,所需字段有:

# 公众号配置(必填)
wx.miniapp.appid = appId
wx.miniapp.secret = @secret
wx.miniapp.token = @token
wx.miniapp.aesKey = @aesKey
wx.miniapp.msgDataFormat = @msgDataFormat                  # 消息格式,XML或者JSON.
# 存储配置redis(可选)
# 注意: 指定redis.host值后不会使用容器注入的redis连接(JedisPool)
wx.miniapp.config-storage.type = Jedis                     # 配置类型: Memory(默认), Jedis, RedisTemplate
wx.miniapp.config-storage.key-prefix = wa                  # 相关redis前缀配置: wa(默认)
wx.miniapp.config-storage.redis.host = 127.0.0.1
wx.miniapp.config-storage.redis.port = 6379
# http客户端配置
wx.miniapp.config-storage.http-client-type=HttpClient      # http客户端类型: HttpClient(默认), OkHttp, JoddHttp
wx.miniapp.config-storage.http-proxy-host=
wx.miniapp.config-storage.http-proxy-port=
wx.miniapp.config-storage.http-proxy-username=
wx.miniapp.config-storage.http-proxy-password=

  根据我们的设计,配置文件中需要增加租户字段,我们需要兼容即使用配置文件来配置微信小程序,又可以使用配置界面将微信小程序配置信息配置到数据库中,同时,增加md5字段配置,用于在读取配置时比较配置信息是否有更改。所以,保存微信小程序配置的数据库设计如下:

CREATE TABLE `t_wechat_miniapp`  (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',`tenant_id` bigint(20) NOT NULL DEFAULT 0 COMMENT '租户id',`miniapp_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '微信小程序名称',`appid` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '微信小程序appid',`secret` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '微信小程序secret',`token` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '微信小程序token',`aes_key` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '微信小程序aesKey',`msg_data_format` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '消息格式,XML或者JSON',`storage_type` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '配置类型: Memory(默认), Jedis, RedisTemplate',`key_prefix` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '相关redis前缀配置: wa(默认)',`redis_host` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'Redis服务器地址',`redis_port` int(11) NULL DEFAULT NULL COMMENT 'Redis服务器端口',`http_client_type` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'http客户端类型: HttpClient(默认), OkHttp, JoddHttp',`http_proxy_host` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'http_proxy_host',`http_proxy_port` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'http_proxy_port',`http_proxy_username` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'http_proxy_username',`http_proxy_password` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'http_proxy_password',`status` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '1' COMMENT '状态 1有效 0禁用',`md5` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'MD5',`comments` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '描述',`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',`creator` bigint(20) NULL DEFAULT NULL COMMENT '创建者',`update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',`operator` bigint(20) NULL DEFAULT NULL COMMENT '更新者',`del_flag` tinyint(2) NOT NULL DEFAULT 0 COMMENT '是否删除',PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '微信小程序配置' ROW_FORMAT = DYNAMIC;
2. 通过代码生成器生成微信小程序配置的增删查改代码

  通过代码生成器根据新设计的表进行CRUD代码生成,详细步骤不再赘述,前面有详细讲解如何根据数据库表设计生成前后端代码。只是这里需要增加业务逻辑处理,以及更新到缓存配置。

  • 新增时,将配置信息添加到Redis,并且需要根据系统是否开启多租户来判断生成缓存key
    /*** 创建微信小程序配置* @param miniapp* @return*/@Overridepublic boolean createMiniapp(CreateMiniappDTO miniapp) {Miniapp miniappEntity = BeanCopierUtils.copyByClass(miniapp, Miniapp.class);try {String miniappEntityStr = JsonUtils.objToJson(miniappEntity);miniappEntity.setMd5(SecureUtil.md5(miniappEntityStr));} catch (Exception e) {log.error("创建微信小程序配置时,md5加密失败:{}", e);throw new BusinessException("创建微信小程序配置时,md5加密失败:" + e);}boolean result = this.save(miniappEntity);if (result){// 更新到缓存Miniapp miniappEntityLocal = this.getById(miniappEntity.getId());MiniappDTO miniappDTO = BeanCopierUtils.copyByClass(miniappEntityLocal, MiniappDTO.class);this.addOrUpdateMiniappCache(miniappDTO);}return result;}
  • 编辑时,需要更新Redis配置信息,因为会有key也同时修改的情况,所以,需要先删除旧的配置信息,再新增新的配置信息
    /*** 更新微信小程序配置* @param miniapp* @return*/@Overridepublic boolean updateMiniapp(UpdateMiniappDTO miniapp) {Miniapp miniappEntity = BeanCopierUtils.copyByClass(miniapp, Miniapp.class);Miniapp miniappEntityOld = this.getById(miniappEntity.getId());try {String miniappEntityStr = JsonUtils.objToJson(miniappEntity);miniappEntity.setMd5(SecureUtil.md5(miniappEntityStr));} catch (Exception e) {log.error("创建微信小程序配置时,md5加密失败:{}", e);throw new BusinessException("创建微信小程序配置时,md5加密失败:" + e);}boolean result = this.updateById(miniappEntity);if (result){// 把旧的删掉MiniappDTO miniappDTOOld = BeanCopierUtils.copyByClass(miniappEntityOld, MiniappDTO.class);this.deleteMiniappCache(miniappDTOOld);// 更新到缓存Miniapp miniappEntityLocal = this.getById(miniappEntity.getId());MiniappDTO miniappDTO = BeanCopierUtils.copyByClass(miniappEntityLocal, MiniappDTO.class);this.addOrUpdateMiniappCache(miniappDTO);}return result;}
  • 删除时,直接根据条件生成缓存key,然后进行删除即可
    /*** 删除微信小程序配置* @param miniappId* @return*/@Overridepublic boolean deleteMiniapp(Long miniappId) {// 从缓存删除Miniapp miniappEntity = this.getById(miniappId);MiniappDTO miniappDTO = BeanCopierUtils.copyByClass(miniappEntity, MiniappDTO.class);this.deleteMiniappCache(miniappDTO);// 从数据库中删除boolean result = this.removeById(miniappId);return result;}
  • 新增/更新缓存的公共方法
    private void addOrUpdateMiniappCache(MiniappDTO miniappDTO) {try {String redisKey = MiniappConstant.WX_MINIAPP_CONFIG_KEY;if (enable) {redisKey = MiniappConstant.WX_MINIAPP_TENANT_CONFIG_KEY + miniappDTO.getAppid();}redisTemplate.opsForHash().put(redisKey, miniappDTO.getTenantId().toString(), JsonUtils.objToJson(miniappDTO));// wxMaService增加configthis.addConfig(miniappDTO);} catch (Exception e) {log.error("初始化微信小程序配置失败:{}" , e);}}
  • 删除缓存的公共方法
    private void deleteMiniappCache(MiniappDTO miniappDTO) {try {String redisKey = MiniappConstant.WX_MINIAPP_CONFIG_KEY;if (enable) {redisKey = MiniappConstant.WX_MINIAPP_TENANT_CONFIG_KEY + miniappDTO.getAppid();}redisTemplate.opsForHash().delete(redisKey, miniappDTO.getTenantId().toString(), JsonUtils.objToJson(miniappDTO));// wxMaService删除configthis.removeConfig(miniappDTO);} catch (Exception e) {log.error("初始化微信小程序配置失败:{}" , e);}}
3. 修改相关配置文件,使微信小程序配置既支持配置文件又支持数据库配置
  • 新增GitEggWxMaRedissonConfigImpl类继承自WxMaRedissonConfigImpl,增加我们需要的租户配置字段:configKey、tenantId、md5。
/*** @author GitEgg* @date 2023/7/21*/
public class GitEggWxMaRedissonConfigImpl extends WxMaRedissonConfigImpl {protected String configKey;protected String tenantId;protected String md5;public GitEggWxMaRedissonConfigImpl(@NonNull RedissonClient redissonClient, String keyPrefix) {super(redissonClient, keyPrefix);}public GitEggWxMaRedissonConfigImpl(@NonNull RedissonClient redissonClient) {super(redissonClient);}
......
}
  • 修改WxMaProperties类,增加我们需要的字段tenantId,因为configKey和md5是系统生成的,只有动态可配置时才会用到这几个字段去判断,这里如果是配置文件配置,修改后生效必须重启系统,所以这里不需要这几个字段。
@Data
@ConfigurationProperties(prefix = "wx.miniapp")
public class WxMaProperties {private List<Config> configs;@Datapublic static class Config {/*** 租户*/private Long tenantId;/*** 设置微信小程序的appid*/private String appid;/*** 设置微信小程序的Secret*/private String secret;/*** 设置微信小程序消息服务器配置的token*/private String token;/*** 设置微信小程序消息服务器配置的EncodingAESKey*/private String aesKey;/*** 消息格式,XML或者JSON*/private String msgDataFormat;}}
  • 修改WxMaConfiguration类,我们使用自己定义的缓存方式和Config类进行相关操作,这里的缓存我们使用Redisson。
    @Beanpublic WxMaService wxMaService() {List<WxMaProperties.Config> configs = this.properties.getConfigs();//已添加缓存配置,如果配置文件没有,那么在缓存新增时,仍然可以setConfigs
//        if (configs == null) {
//            throw new WxRuntimeException("大哥,拜托先看下项目首页的说明(readme文件),添加下相关配置,注意别配错了!");
//        }WxMaService maService = new WxMaServiceImpl();if (null != configs){maService.setMultiConfigs(configs.stream().map(a -> {
//                    WxMaDefaultConfigImpl config = new WxMaDefaultConfigImpl();
//                    WxMaDefaultConfigImpl config = new WxMaRedisConfigImpl(new JedisPool());GitEggWxMaRedissonConfigImpl config = new GitEggWxMaRedissonConfigImpl(redissonClient);// 使用上面的配置时,需要同时引入jedis-lock的依赖,否则会报类无法找到的异常config.setTenantId(null != a.getTenantId() ? a.getTenantId().toString() : AuthConstant.DEFAULT_TENANT_ID.toString());config.setConfigKey(config.getTenantId() + StrPool.UNDERLINE + a.getAppid());config.setAppid(a.getAppid());config.setSecret(a.getSecret());config.setToken(a.getToken());config.setAesKey(a.getAesKey());config.setMsgDataFormat(a.getMsgDataFormat());return config;}).collect(Collectors.toMap( GitEggWxMaRedissonConfigImpl::getConfigKey, a -> a, (o, n) -> o)));}return maService;}

二、服务启动时加载微信小程序配置信息

1. 新增加载方法,服务启动时加载配置数据需要排除多租户插件,之所以在启动时加载配置信息到缓存,是因为配置的微服务和小程序相关功能的服务不是同一个服务,所以将缓存作为相关配置的存储。
  • MiniappMapper中新增initMiniappList数据库查询方法,一定要加@InterceptorIgnore(tenantLine = “true”)注解,表示此查询不需要多租户控制,在服务启动时不区分租户加载所有配置。
    /*** 排除多租户插件查询微信配置列表* @param miniappDTO* @return*/@InterceptorIgnore(tenantLine = "true")List<MiniappDTO> initMiniappList(@Param("miniapp") QueryMiniappDTO miniappDTO);
    <!-- 不区分租户查询微信小程序配置信息 --><select id="getMiniapp" resultType="com.gitegg.boot.extension.wx.miniapp.dto.MiniappDTO" parameterType="com.gitegg.boot.extension.wx.miniapp.dto.QueryMiniappDTO">SELECT<include refid="Base_Column_List"/>FROM t_wechat_miniappWHERE del_flag = 0<if test="miniapp.miniappName != null and miniapp.miniappName != ''">AND miniapp_name = #{miniapp.miniappName}</if><if test="miniapp.appid != null and miniapp.appid != ''">AND appid = #{miniapp.appid}</if><if test="miniapp.secret != null and miniapp.secret != ''">AND secret = #{miniapp.secret}</if><if test="miniapp.status != null and miniapp.status != ''">AND status = #{miniapp.status}</if>ORDER BY id DESC</select>
  • MiniappServiceImpl中新增initMiniappList接口的实现方法,缓存配置的增删查改方法也在此类中实现,这里不再赘述,更多了解可以查看框架代码。
    /*** 初始化微信小程序配置表列表* @return*/@Overridepublic void initMiniappList() {QueryMiniappDTO miniappDTO = new QueryMiniappDTO();miniappDTO.setStatus(String.valueOf(GitEggConstant.ENABLE));// 这里初始化所有的配置,不再只初始化已启用的配置List<MiniappDTO> miniappInfoList = miniappMapper.initMiniappList(miniappDTO);// 判断是否开启了租户模式,如果开启了,那么需要按租户进行分类存储if (enable) {Map<String, List<MiniappDTO>> miniappListMap =miniappInfoList.stream().collect(Collectors.groupingBy(MiniappDTO::getAppid));miniappListMap.forEach((key, value) -> {String redisKey = MiniappConstant.WX_MINIAPP_TENANT_CONFIG_KEY + key;redisTemplate.delete(redisKey);addMiniapp(redisKey, value);});} else {redisTemplate.delete(MiniappConstant.WX_MINIAPP_CONFIG_KEY);addMiniapp(MiniappConstant.WX_MINIAPP_CONFIG_KEY, miniappInfoList);}}
  • 在InitExtensionCacheRunner系统配置加载类中新增initMiniappList的调用
/*** 容器启动完成加载扩展信息数据到缓存* @author GitEgg*/
@Slf4j
@RequiredArgsConstructor(onConstructor_ = @Autowired)
@Component
public class InitExtensionCacheRunner implements CommandLineRunner {private final IJustAuthConfigService justAuthConfigService;private final IJustAuthSourceService justAuthSourceService;private final IMailChannelService mailChannelService;private final IMiniappService miniappService;@Overridepublic void run(String... args) {log.info("InitExtensionCacheRunner running");// 初始化第三方登录主配置justAuthConfigService.initJustAuthConfigList();// 初始化第三方登录 第三方配置justAuthSourceService.initJustAuthSourceList();// 初始化邮件配置信息mailChannelService.initMailChannelList();// 初始化微信配置信息miniappService.initMiniappList();}
}
2. 实现动态选择某个租户微信小程序接口的方法,我们需要在保证原先读取配置文件的方式仍然可用的基础上扩展读取数据库缓存配置信息,所以,在接口实现时需要充分考虑原先配置方式可用。
  • 通过appid获取组装后的key值,在WxMaService中存储着默认以appid为key值的配置configMap,这里我们将key值修改为tenantId_appid的格式,作为多租户的扩展,同时在获取配置信息时,也需要传入多租户信息。
  • 如果前端传了租户,那么先使用前端的租户,如果没有传租户,那么从系统中查询租户
  • 从缓存获取配置对象,如果md5配置和系统配置不一样,那么需要重新add
  • 缓存配置中没有也需要直接返回,因为有可能是配置文件配置的
  • 取缓存中所有appid的配置租户,如果存在多个租户,那么提示错误,让前端选择租户;如果只有一个租户,那么返回
    /*** 通过appid获取appid,忽略租户插件* @param miniappId* @return*/@Overridepublic String getMiniappId(String miniappId) {if (enable) {// 如果前端传了租户,那么先使用前端的租户,如果没有传租户,那么从系统中查询租户String tenantId = GitEggAuthUtils.getTenantId();if (!StringUtils.isEmpty(tenantId)){String miniappStr = (String) redisTemplate.opsForHash().get(MiniappConstant.WX_MINIAPP_TENANT_CONFIG_KEY + miniappId, tenantId);if (!StringUtils.isEmpty(miniappStr)){// 转为系统配置对象try {// 从缓存获取配置对象,如果md5配置和系统配置不一样,那么需要重新addMiniappDTO miniappDTO = JsonUtils.jsonToPojo(miniappStr, MiniappDTO.class);return this.ifConfig(miniappDTO);} catch (Exception e) {log.error("获取微信小程序配置时发生异常:{}", e);throw new BusinessException("获取微信小程序配置时发生异常。");}}// 缓存配置中没有也需要直接返回,因为有可能是配置文件配置的return tenantId + StrPool.UNDERLINE + miniappId;} else {String redisKey = MiniappConstant.WX_MINIAPP_TENANT_CONFIG_KEY + miniappId;// 取缓存中所有appid的配置租户,如果存在多个租户,那么提示错误,让前端选择租户;如果只有一个租户,那么返回List<Object> values = redisTemplate.opsForHash().values(redisKey);if (!CollectionUtils.isEmpty(values)){if (values.size() > GitEggConstant.Number.ONE){throw new BusinessException("此小程序配置在多个租户下,请选择所需要操作的租户。");}String miniappConfig = (String) values.stream().findFirst().orElse(null);try {MiniappDTO miniappConfigDTO = JsonUtils.jsonToPojo(miniappConfig, MiniappDTO.class);return this.ifConfig(miniappConfigDTO);} catch (Exception e) {log.error("获取缓存小程序配置失败:{}", e);throw new BusinessException("小程序已被禁用,请联系管理员");}}else{return AuthConstant.DEFAULT_TENANT_ID + StrPool.UNDERLINE + miniappId;}}} else {return AuthConstant.DEFAULT_TENANT_ID + StrPool.UNDERLINE + miniappId;}}
3. 修改原先的切换租户方法调用,在WxMaUserController中,将原来的wxMaService.switchover(appid)修改为我们自己的实现miniappService.switchover(appid),其它有切换租户调用的都改为此方法。
        if (!miniappService.switchover(appid)) {throw new IllegalArgumentException(String.format("未找到对应appid=[%s]的配置,请核实!", appid));}
4. 微信小程序前端代码不需要修改,还是按照前面章节的说明进行调用即可。在请求中加入appid,在请求头中加入租户id。
import request from '@/common/utils/request'const wechatLoginApi = {Login: '/extension/wx/user/'
}export default wechatLoginApi/*** 微信登录* @param {Object} appId* @param {Object} parameter*/
export function wechatLogin (appId, parameter) {return request({url: wechatLoginApi.Login + appId + '/login',method: 'get',params: parameter})
}/*** 获取微信信息* @param {Object} appId* @param {Object} parameter*/
export function wechatInfo (appId, parameter) {return request({url: wechatLoginApi.Login + appId + '/info',method: 'get',params: parameter})
}/*** 获取手机号* @param {Object} appId* @param {Object} parameter*/
export function wechatPhone (appId, parameter) {return request({url: wechatLoginApi.Login + appId + '/phone',method: 'get',params: parameter})
}

  在修改配置时,一定需要注意权限问题,一般情况下,在不同的租户下不允许配置相同的微信小程序,因为appid是唯一的,在发布微信小程序后,微信小程序是唯一的。当然也有特殊的情况,比如,同一个小程序作为多个租户相同的商户管理端,那么在此时,需要让用户在前端选择输入租户标识以确定登录用户属于那个租户,即多个租户共用同一个微信小程序。

GitEgg-Cloud是一款基于SpringCloud整合搭建的企业级微服务应用开发框架,开源项目地址:

Gitee: https://gitee.com/wmz1930/GitEgg

GitHub: https://github.com/wmz1930/GitEgg

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/12330.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C#如何使用SQLite数据库?

文章目录 0.引言1.SQLite工具准备2.创建窗体项目并添加SQLite的命名空间3.编写使用SQLite代码4.结果展示 0.引言 SQLite是一个轻量级的嵌入式数据库&#xff0c;它的库文件非常小巧&#xff0c;不需要独立的服务器进程或配置。这使得它非常适合在资源受限的环境中使用&#xff…

【Terraform学习】Terraform-AWS部署快速入门(快速入门)

Terraform-AWS部署快速入门 实验步骤 连接到 Terraform 环境 SSH 连接到Terraform 环境(名为MyEC2Instance的实例) 在 Amazon Web Services &#xff08;AWS&#xff09; 上预置 EC2 实例 用于描述 Terraform 中基础结构的文件集称为 Terraform 配置。您将编写一个配置来定义…

防御第三天

1.总结当堂NAT与双机热备原理&#xff0c;形成思维导图 2.完成课堂NAT与双机热备实验 fw1: <USG6000V1>sy [USG6000V1]int g0/0/0 [USG6000V1-GigabitEthernet0/0/0]ip add 192.168.18.2 24 [USG6000V1-GigabitEthernet0/0/0]service-manage all permit (地址无所谓&…

设计模式适合用于解决特定的软件设计问题呢

当我们在开发软件时&#xff0c;经常会遇到各种各样的问题和挑战&#xff0c;例如如何处理对象之间的关系、如何实现复杂的业务逻辑、如何处理并发访问等。这些问题都是软件设计中经常遇到的问题&#xff0c;而设计模式就是为了解决这些问题而诞生的。 以下是一些常见的软件设…

SpringCloud学习路线(11)——分布式搜索ElasticSeach场景使用

一、DSL查询文档 &#xff08;一&#xff09;DSL查询分类 ES提供了基于JSON的DSL来定义查询。 1、常见查询类型&#xff1a; 查询所有&#xff1a; 查询出所有的数据&#xff0c;例如&#xff0c;match_all全文检索&#xff08;full text&#xff09;查询&#xff1a; 利用…

leetcode 50. Pow(x, n)(x的n次方)

求x的n次方。 思路&#xff1a; 第一个想到的思路是x和它自己乘n次&#xff0c; 但是这样做会面临一些问题&#xff1a; 如果是简单的n很小的情况还好&#xff0c;但是可以看到n的取值横跨整个整数范围&#xff0c; 如果n非常大&#xff0c;一次一次乘x效率低是其一。 一般来…

探索网页原型设计:构建出色的用户体验

在当今数字化时代&#xff0c;用户对网页体验的要求日益提高。在网页设计过程中&#xff0c;扮演着至关重要的角色。通过网页原型设计&#xff0c;产品经理能够更好地展示和传达网页的整体布局、导航结构、元素位置和交互效果&#xff0c;从而使团队成员更清晰地了解设计意图&a…

mapboxGL中楼层与室内地图的结合展示

概述 质量不够&#xff0c;数量来凑&#xff0c;没错&#xff0c;本文就是来凑数的。前面的几篇文章实现了楼栋与楼层单体化的展示、室内地图的展示&#xff0c;本文结合前面的几篇文章&#xff0c;做一个综合的展示效果。 实现效果 实现 1. 数据处理 要实现上图所示的效果…

Excel透视表与python实现

目录 一、Excel透视表 1、源数据 2、数据总分析 3、数据top分析 二、python实现 1、第一张表演示 2、第二张表演示 一、Excel透视表 1、源数据 1&#xff09;四个类目&#xff0c;每类50条数据 2&#xff09;数据内容 2、数据总分析 1&#xff09;选择要分析的字段&…

leetcode做题笔记46

给定一个不含重复数字的数组 nums &#xff0c;返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。 思路一&#xff1a;回溯 void swap(int *nums,int index1,int index2) {int temp nums[index1];nums[index1] nums[index2];nums[index2] temp; }void prem(int* nu…

已经重新安装了MySQL,第一次应该如何登录root用户

如果已经重新安装了MySQL&#xff0c;第一次登录root用户需要按照以下步骤进行&#xff1a; 1. 打开命令行终端或控制台。 2. 通过一下命令启动MySQL服务器&#xff1a; sudo systemctl start mysql3. 登录MySQL服务器&#xff1a; sudo mysql -u root 如果服务器在本地主…

vue项目登录页面实现记住用户名和密码

vue项目登录页面实现记住用户名和密码 记录一下实现的逻辑&#xff0c;应该分两步来理解这个逻辑 首次登录&#xff0c;页面没有用户的登录信息&#xff0c;实现逻辑如下&#xff1a; 用户输入用户名和密码登录&#xff0c;用户信息为名为form的响应式对象&#xff0c;v-model…

Linux Day03

一、基础命令(在Linux Day02基础上补充) 1.10 find find 搜索路径 -name 文件名 按文件名字搜索 find 搜索路径 -cmin -n 搜索过去n分钟内修改的文件 find 搜索路径 -ctime -n搜索过去n分钟内修改的文件 1&#xff09;按文件名字 2&#xff09;按时间 1.11 grep 在文件中过…

linux+Jenkins+飞书机器人发送通知(带签名)

文章目录 如何使用在linux 上安装python 环境发送消息python脚本把脚本上传倒linux上 jenkins 上执行脚本 如何使用 自定义机器人使用指南飞书官网https://open.feishu.cn/document/client-docs/bot-v3/add-custom-bot 在linux 上安装python 环境 yum install python3 python…

yolov5检测到的框画到原图上

1.把检测到的框画到原图上 import warnings warnings.filterwarnings(ignore) warnings.simplefilter(ignore) import torch import cv2 import numpy as np import requests import torchvision.transforms as transformsfrom models.common

“深入解析Spring Boot:从入门到进阶“

标题&#xff1a;深入解析Spring Boot&#xff1a;从入门到进阶 摘要&#xff1a;本文将深入解析Spring Boot框架&#xff0c;从入门到进阶&#xff0c;帮助开发者更好地理解和应用Spring Boot。内容包括Spring Boot的基本概念、核心特性、常用组件和高级用法&#xff0c;并提…

如何在3ds max中创建可用于真人场景的巨型机器人:第 5 部分

推荐&#xff1a; NSDT场景编辑器助你快速搭建可二次开发的3D应用场景 1. After Effects 中的项目设置 步骤 1 打开“后效”。 打开后效果 步骤 2 我有真人版 我在After Effects中导入的素材。这是将 用作与机器人动画合成的背景素材。 实景镜头 步骤 3 有背景 选定的素材…

Mindar.JS——实现AR图像追踪插入图片或视频

Mindar.JS使用方式 注意&#xff1a;此篇文章需要启动https才可调用相机权限 图像追踪示例 需要用到两个js库 <script src"./js/aframe.min.js"></script><script src"./js/mindar-image-aframe.prod.js"></script>下面看一下标签…

原生js vue react通用的递归函数

&#x1f642;博主&#xff1a;锅盖哒 &#x1f642;文章核心&#xff1a;原生js vue react通用的递归函数 目录大纲 1.递归函数的由来 2.代码逻辑 1.递归函数的由来 递归函数的由来可以追溯到数学中的递归概念和数学归纳法。 在数学中&#xff0c;递归是指通过定义基本情况和…

vue+leaflet笔记之地图聚合

vueleaflet笔记之地图聚合 文章目录 vueleaflet笔记之地图聚合开发环境代码简介插件简介与安装使用简介 详细源码(Vue3) 本文介绍了Web端使用Leaflet开发库进行地图聚合查询的一种方法 (底图来源:中科星图)&#xff0c;结合Leaflet.markercluster插件能够快速的实现地图聚合查询…