分类: AI技术选型

  • AgileBoot 为中小型项目设计的 Spring Boot 快速开发框架

    产品概述

    AgileBoot 是一个基于 Spring Boot 的快速开发框架,专为中小型项目设计 1 。该项目采用分层架构模式,具有清晰的关注点分离,旨在创建可维护和可扩展的代码库 2 。

    系统架构

    AgileBoot 采用五层模块化架构:

    graph TD
        subgraph "客户端访问层"
            AdminModule["agileboot-admin<br>(管理后台入口)"]
            ApiModule["agileboot-api<br>(外部API入口)"]
        end
    
        subgraph "业务逻辑层"
            DomainModule["agileboot-domain<br>(核心业务逻辑)"]
        end
    
        subgraph "基础设施层"
            InfraModule["agileboot-infrastructure<br>(配置和集成)"]
        end
    
        subgraph "通用工具"
            CommonModule["agileboot-common<br>(基础工具)"]
        end
    
        AdminModule --> DomainModule
        ApiModule --> DomainModule
        DomainModule --> InfraModule
        InfraModule --> CommonModule
    

    模块描述依赖关系
    agileboot-admin管理后台接口模块,包含后台管理的控制器和API端点 3Domain
    agileboot-api开放接口模块,为客户端应用提供RESTful端点 4Domain
    agileboot-domain核心业务逻辑,包含领域模型、应用服务和数据库操作 5Infrastructure
    agileboot-infrastructure基础设施配置,外部系统集成和横切关注点 6Common
    agileboot-common共享工具、基础类和通用组件 7

    技术栈

    类别技术版本用途
    核心框架Spring Boot2.7.x应用框架 8
    安全Spring Security认证和授权
     JWT0.9.1基于令牌的认证 9
    数据库MySQL8.x主数据库
     MyBatis Plus3.5.2ORM框架和代码生成 10
     Druid1.2.8数据库连接池
    缓存Redis分布式缓存
     Guava31.0.1本地缓存 11
    工具Hutool5.7.22Java工具库
     Lombok1.18.24减少样板代码
    文档SpringDoc1.6.14API文档生成 12

    核心功能

    1. 用户管理

    ● 系统用户配置和属性管理

    ● 用户个人资料管理 13

    ● 头像上传功能 14

    2. 角色管理

    ● 基于角色的权限分配

    ● 数据范围控制 15

    3. 菜单管理

    ● 动态菜单配置

    ● 权限控制 16

    4. 岗位管理

    ● 岗位信息的增删查改 17

    5. 系统监控

    ● 操作日志记录 18

    ● 登录日志记录

    ● 系统资源监控

    安全特性

    JWT 认证系统

    系统使用 JWT 进行无状态认证,支持多终端认证系统 19 。

    Spring Security 集成

    完整的 Spring Security 配置,包括:

    ● 登录流程处理 20

    ● 权限验证

    ● 登出处理

    注解式权限控制

    ● @PreAuthorize 注解进行方法级权限控制

    ● 数据权限拦截

    ● 菜单权限拦截 21

    缓存系统

    AgileBoot 实现了多级缓存策略:

    三级缓存架构

    1.  Map 缓存: 简单内存缓存,用于轻量级数据

    2.  Guava 缓存: 本地内存缓存,具有过期策略

    3.  Redis 缓存: 分布式缓存,用于用户会话和共享数据 11

    缓存应用

    ● 权限判断使用多级缓存

    ● 用户登录信息缓存

    ● 字典数据缓存

    项目结构设计

    领域驱动设计

    项目采用 CQRS(命令查询责任分离)理念,将查询和操作分开处理 22 :

    查询流程: Controller → Query → ApplicationService → Service → Mapper

    操作流程: Controller → Command → ApplicationService → Model → save/update

    标准模块结构

    agileboot-domain
    ├── module-name
    │ ├── command (命令参数接收模型)
    │ ├── dto (返回数据类)
    │ ├── db
    │ │ ├── entity (实体类)
    │ │ ├── service (DB Service)
    │ │ ├── mapper (DB Dao)
    │ ├── model (领域模型类)
    │ ├── query (查询参数模型)
    │ └── ModuleApplicationService (应用服务)

    特色功能

    注解式功能

    ● 主从数据库切换

    ● 请求限流

    ● 重复请求拦截 23

    动态权限

    ● 支持加载动态权限菜单

    ● 实时权限控制,无需重启 24

    测试覆盖

    项目包含大量的单元测试和集成测试,确保业务逻辑正确 25 。

    快速开始

    环境要求

    ● JDK

    ● MySQL

    ● Redis

    ● Node.js (前端)

    启动步骤

    1.  克隆项目代码

    2.  导入数据库脚本

    3.  配置数据库和Redis连接

    4.  执行 mvn install

    5.  启动 AgileBootAdminApplication 26

    嵌入式模式

    支持无需外部 MySQL 和 Redis 的启动方式,便于开发测试 27 。

    在线体验

    ● 演示地址:www.agileboot.vip 或 www.agileboot.cc

    ● 默认账号:admin/admin123 28

    Notes

    AgileBoot 是一个完全重构的项目,基于 Ruoyi 项目进行了大量改进,包括代码规范、架构优化、性能提升等方面。项目采用现代化的开发理念和实践,提供了完整的后台管理解决方案。

    Wiki pages you might want to explore:

    ● Overview (valarchie/AgileBoot-Back-End)

    ● Controller Layer (valarchie/AgileBoot-Back-End)

    Citations

    File: README.md (L31-36)

    * 后端采用Spring Boot、Spring Security & Jwt、Redis & MySql、Mybatis Plus、Hutool工具包。 * 权限认证使用Jwt,支持多终端认证系统。 * 支持注解式主从数据库切换,注解式请求限流,注解式重复请求拦截。 * 支持注解式菜单权限拦截,注解式数据权限拦截。 * 支持加载动态权限菜单,实时权限控制。 * ***有大量的单元测试,集成测试覆盖确保业务逻辑正确***。 File: README.md (L48-52)

    ## 💥 在线体验 💥 演示地址: – www.agileboot.vip – www.agileboot.cc > 账号密码:admin/admin123 File:README.md (L99-102)

    – 优化Redis缓存类,封装各个业务缓存,提供多级缓存实现(Redis+Guava) – 提供三个层级的缓存供使用者调用(Map,Guava,Redis使用者可依情况选择使用哪个缓存类) – 权限判断使用多级缓存 – IP地址查询引入离线包 File:README.md (L123-129)

    | 技术 | 说明 | 版本 | |—————-|—————–|——————-| | `springboot` | Java项目必备框架 | 2.7 | | `druid` | alibaba数据库连接池 | 1.2.8 | | `springdoc` | 文档生成 | 3.0.0 | | `mybatis-plus` | 数据库框架 | 3.5.2 | | `hutool` | 国产工具包(简单易用) | 3.5.2 | File: README.md (L141-171)

    #### 前置准备: 下载前后端代码  git clone https://github.com/valarchie/AgileBoot-Back-End

    git clone https://github.com/valarchie/AgileBoot-Front-End

     
    #### 安装好Mysql和Redis
     
     
    #### 后端启动

    1.  生成所需的数据库表

    2.  找到后端项目根目录下的sql目录中的agileboot_xxxxx.sql脚本文件(取最新的sql文件)。 导入到你新建的数据库中。在admin模块底下,找到resource目录下的application-dev.yml文件

    3.  配置数据库以及Redis的 地址、端口、账号密码在根目录执行mvn install

    4.  找到agileboot-admin模块中的AgileBootAdminApplication启动类,直接启动即可

    5.  当出现以下字样即为启动成功

    | | | __ _ _ __ | | _ _ _ __ ___ _ _ ___ ___ ___ ___ ___ / | _ _ | || |

    _ \ | |/ _` || ‘|| | | | | || ’ \ / || | | | / |/ |/ _ / |/ || | | | | || || |

    ) || || (| || | | | | || || |) | _ | |_| || (| (|  /_ \ |  || || || |||

    |/ _|_,||| _| _,|| ./ |/ _,| _|_|_||/|/|| _,|||()

    |_|File: README.md (L194-210)

    1.  > 对于想要尝试全栈项目的前端人员,这边提供更简便的后端启动方式,无需配置Mysql和Redis直接启动 #### 无Mysql/Redis 后端启动 找到agilboot-admin模块下的resource文件中的application.yml文件

    2.  配置以下两个值

    spring.profiles.active: basic,dev

    改为

    spring.profiles.active: basic,testagileboot.embedded.mysql: false

    agileboot.embedded.redis: false

    改为

    agileboot.embedded.mysql: true

    agileboot.embedded.redis: true请注意:高版本的MacOS系统,无法启动内置的Redis

     
    **File:** README.md (L241-274)
    ```markdown
     

    agileboot

    ├── agileboot-admin – 管理后台接口模块(供后台调用)

    ├── agileboot-api – 开放接口模块(供客户端调用)

    ├── agileboot-common – 精简基础工具模块

    ├── agileboot-infrastructure – 基础设施模块(主要是配置和集成,不包含业务逻辑)

    ├── agileboot-domain – 业务模块

    ├ ├── user – 用户模块(举例)

    ├ ├── command – 命令参数接收模型(命令)

    ├ ├── dto – 返回数据类

    ├ ├── db – DB操作类

    ├ ├── entity – 实体类

    ├ ├── service – DB Service

    ├ ├── mapper – DB Dao

    ├ ├── model – 领域模型类

    ├ ├── query – 查询参数模型(查询)

    │ ├────── UserApplicationService – 应用服务(事务层,操作领域模型类完成业务逻辑)

     
    ### 代码流转
     
    请求分为两类:一类是查询,一类是操作(即对数据有进行更新)。
     
    **查询**:Controller > xxxQuery > xxxApplicationService > xxxService(Db) > xxxMapper
    **操作**:Controller > xxxCommand > xxxApplicationService > xxxModel(处理逻辑) > save 或者 update (本项目直接采用JPA的方式进行插入已经更新数据)
     
    这是借鉴CQRS的开发理念,将查询和操作分开处理。操作类的业务实现借鉴了DDD战术设计的理念,使用领域类,工厂类更面向对象的实现逻辑。
    如果你不太适应这样的开发模式的话。可以在domain模块中按照你之前从Controller->Service->DAO的模式进行开发。it is up to you.

    File: agileboot-infrastructure/src/main/java/com/agileboot/infrastructure/config/SpringDocConfig.java (L18-27)

        public OpenAPI agileBootApi() {
    return new OpenAPI()
    .info(new Info().title("Agileboot后台管理系统")
    .description("Agileboot API 演示")
    .version("v1.8.0")
    .license(new License().name("MIT 3.0").url("https://github.com/valarchie/AgileBoot-Back-End")))
    .externalDocs(new ExternalDocumentation()
    .description("Agileboot后台管理系统接口文档")
    .url("https://juejin.cn/column/7159946528827080734"));
    }

    File: agileboot-admin/src/main/java/com/agileboot/admin/controller/system/SysProfileController.java (L44-66)

        /**
    * 个人信息
    */
    @Operation(summary = "获取个人信息")
    @GetMapping
    public ResponseDTO<UserProfileDTO> profile() {
    SystemLoginUser user = AuthenticationUtils.getSystemLoginUser();
    UserProfileDTO userProfile = userApplicationService.getUserProfile(user.getUserId());
    return ResponseDTO.ok(userProfile);
    }
     
    /**
    * 修改用户
    */
    @Operation(summary = "修改个人信息")
    @AccessLog(title = "个人信息", businessType = BusinessTypeEnum.MODIFY)
    @PutMapping
    public ResponseDTO<Void> updateProfile(@RequestBody UpdateProfileCommand command) {
    SystemLoginUser loginUser = AuthenticationUtils.getSystemLoginUser();
    command.setUserId(loginUser.getUserId());
    userApplicationService.updateUserProfile(command);
    return ResponseDTO.ok();
    }

    File: agileboot-admin/src/main/java/com/agileboot/admin/controller/system/SysProfileController.java (L81-96)

        /**
    * 头像上传
    */
    @Operation(summary = "修改个人头像")
    @AccessLog(title = "用户头像", businessType = BusinessTypeEnum.MODIFY)
    @PostMapping("/avatar")
    public ResponseDTO<UploadFileDTO> avatar(@RequestParam("avatarfile") MultipartFile file) {
    if (file.isEmpty()) {
    throw new ApiException(ErrorCode.Business.USER_UPLOAD_FILE_FAILED);
    }
    SystemLoginUser loginUser = AuthenticationUtils.getSystemLoginUser();
    String avatarUrl = FileUploadUtils.upload(UploadSubDir.AVATAR_PATH, file);
     
    userApplicationService.updateUserAvatar(new UpdateUserAvatarCommand(loginUser.getUserId(), avatarUrl));
    return ResponseDTO.ok(new UploadFileDTO(avatarUrl));
    }

    File: agileboot-admin/src/main/java/com/agileboot/admin/controller/system/SysRoleController.java (L46-112)

    public class SysRoleController extends BaseController {
     
    private final RoleApplicationService roleApplicationService;
     
    @Operation(summary = "角色列表")
    @PreAuthorize("@permission.has('system:role:list')")
    @GetMapping("/list")
    public ResponseDTO<PageDTO<RoleDTO>> list(RoleQuery query) {
    PageDTO<RoleDTO> pageDTO = roleApplicationService.getRoleList(query);
    return ResponseDTO.ok(pageDTO);
    }
     
    @Operation(summary = "角色列表导出")
    @AccessLog(title = "角色管理", businessType = BusinessTypeEnum.EXPORT)
    @PreAuthorize("@permission.has('system:role:export')")
    @PostMapping("/export")
    public void export(HttpServletResponse response, RoleQuery query) {
    PageDTO<RoleDTO> pageDTO = roleApplicationService.getRoleList(query);
    CustomExcelUtil.writeToResponse(pageDTO.getRows(), RoleDTO.class, response);
    }
     
    /**
    * 根据角色编号获取详细信息
    */
    @Operation(summary = "角色详情")
    @PreAuthorize("@permission.has('system:role:query')")
    @GetMapping(value = "/{roleId}")
    public ResponseDTO<RoleDTO> getInfo(@PathVariable @NotNull Long roleId) {
    RoleDTO roleInfo = roleApplicationService.getRoleInfo(roleId);
    return ResponseDTO.ok(roleInfo);
    }
     
    /**
    * 新增角色
    */
    @Operation(summary = "添加角色")
    @PreAuthorize("@permission.has('system:role:add')")
    @AccessLog(title = "角色管理", businessType = BusinessTypeEnum.ADD)
    @PostMapping
    public ResponseDTO<Void> add(@RequestBody AddRoleCommand addCommand) {
    roleApplicationService.addRole(addCommand);
    return ResponseDTO.ok();
    }
     
    /**
    * 移除角色
    */
    @Operation(summary = "删除角色")
    @PreAuthorize("@permission.has('system:role:remove')")
    @AccessLog(title = "角色管理", businessType = BusinessTypeEnum.DELETE)
    @DeleteMapping(value = "/{roleId}")
    public ResponseDTO<Void> remove(@PathVariable("roleId") List<Long> roleIds) {
    roleApplicationService.deleteRoleByBulk(roleIds);
    return ResponseDTO.ok();
    }
     
    /**
    * 修改保存角色
    */
    @Operation(summary = "修改角色")
    @PreAuthorize("@permission.has('system:role:edit')")
    @AccessLog(title = "角色管理", businessType = BusinessTypeEnum.MODIFY)
    @PutMapping
    public ResponseDTO<Void> edit(@Validated @RequestBody UpdateRoleCommand updateCommand) {
    roleApplicationService.updateRole(updateCommand);
    return ResponseDTO.ok();
    }

    File: agileboot-domain/src/main/java/com/agileboot/domain/system/menu/db/SysMenuMapper.java (L25-33)

        @Select("SELECT DISTINCT m.* "
    + "FROM sys_menu m "
    + " LEFT JOIN sys_role_menu rm ON m.menu_id = rm.menu_id "
    + " LEFT JOIN sys_user u ON rm.role_id = u.role_id "
    + "WHERE u.user_id = #{userId} "
    + " AND m.status = 1 "
    + " AND m.deleted = 0 "
    + "ORDER BY m.parent_id")
    List<SysMenuEntity> selectMenuListByUserId(@Param("userId")Long userId);

    File: agileboot-admin/src/main/java/com/agileboot/admin/controller/system/SysPostController.java (L34-122)

    /**
    * 岗位信息操作处理
    *
    * @author ruoyi
    */
    @Tag(name = "职位API", description = "职位相关的增删查改")
    @RestController
    @RequestMapping("/system/post")
    @Validated
    @RequiredArgsConstructor
    public class SysPostController extends BaseController {
     
    private final PostApplicationService postApplicationService;
     
    /**
    * 获取岗位列表
    */
    @Operation(summary = "职位列表")
    @PreAuthorize("@permission.has('system:post:list')")
    @GetMapping("/list")
    public ResponseDTO<PageDTO<PostDTO>> list(PostQuery query) {
    PageDTO<PostDTO> pageDTO = postApplicationService.getPostList(query);
    return ResponseDTO.ok(pageDTO);
    }
     
    /**
    * 导出查询到的所有岗位信息到excel文件
    * @param response http响应
    * @param query 查询参数
    * @author Kevin Zhang
    * @date 2023-10-02
    */
    @Operation(summary = "职位列表导出")
    @AccessLog(title = "岗位管理", businessType = BusinessTypeEnum.EXPORT)
    @PreAuthorize("@permission.has('system:post:export')")
    @GetMapping("/excel")
    public void export(HttpServletResponse response, PostQuery query) {
    List<PostDTO> all = postApplicationService.getPostListAll(query);
    CustomExcelUtil.writeToResponse(all, PostDTO.class, response);
    }
     
    /**
    * 根据岗位编号获取详细信息
    */
    @Operation(summary = "职位详情")
    @PreAuthorize("@permission.has('system:post:query')")
    @GetMapping(value = "/{postId}")
    public ResponseDTO<PostDTO> getInfo(@PathVariable Long postId) {
    PostDTO post = postApplicationService.getPostInfo(postId);
    return ResponseDTO.ok(post);
    }
     
    /**
    * 新增岗位
    */
    @Operation(summary = "添加职位")
    @PreAuthorize("@permission.has('system:post:add')")
    @AccessLog(title = "岗位管理", businessType = BusinessTypeEnum.ADD)
    @PostMapping
    public ResponseDTO<Void> add(@RequestBody AddPostCommand addCommand) {
    postApplicationService.addPost(addCommand);
    return ResponseDTO.ok();
    }
     
    /**
    * 修改岗位
    */
    @Operation(summary = "修改职位")
    @PreAuthorize("@permission.has('system:post:edit')")
    @AccessLog(title = "岗位管理", businessType = BusinessTypeEnum.MODIFY)
    @PutMapping
    public ResponseDTO<Void> edit(@RequestBody UpdatePostCommand updateCommand) {
    postApplicationService.updatePost(updateCommand);
    return ResponseDTO.ok();
    }
     
    /**
    * 删除岗位
    */
    @Operation(summary = "删除职位")
    @PreAuthorize("@permission.has('system:post:remove')")
    @AccessLog(title = "岗位管理", businessType = BusinessTypeEnum.DELETE)
    @DeleteMapping
    public ResponseDTO<Void> remove(@RequestParam @NotNull @NotEmpty List<Long> ids) {
    postApplicationService.deletePost(new BulkOperationCommand<>(ids));
    return ResponseDTO.ok();
    }
     
    }

    File: agileboot-domain/src/main/java/com/agileboot/domain/system/log/LogApplicationService.java (L32-52)

        public PageDTO<LoginLogDTO> getLoginInfoList(LoginLogQuery query) {
    Page<SysLoginInfoEntity> page = loginInfoService.page(query.toPage(), query.toQueryWrapper());
    List<LoginLogDTO> records = page.getRecords().stream().map(LoginLogDTO::new).collect(Collectors.toList());
    return new PageDTO<>(records, page.getTotal());
    }
     
    public void deleteLoginInfo(BulkOperationCommand<Long> deleteCommand) {
    QueryWrapper<SysLoginInfoEntity> queryWrapper = new QueryWrapper<>();
    queryWrapper.in("info_id", deleteCommand.getIds());
    loginInfoService.remove(queryWrapper);
    }
     
    public PageDTO<OperationLogDTO> getOperationLogList(OperationLogQuery query) {
    Page<SysOperationLogEntity> page = operationLogService.page(query.toPage(), query.toQueryWrapper());
    List<OperationLogDTO> records = page.getRecords().stream().map(OperationLogDTO::new).collect(Collectors.toList());
    return new PageDTO<>(records, page.getTotal());
    }
     
    public void deleteOperationLog(BulkOperationCommand<Long> deleteCommand) {
    operationLogService.removeBatchByIds(deleteCommand.getIds());
    }

    File: agileboot-admin/src/main/java/com/agileboot/admin/customize/service/login/TokenService.java (L132-161)

        private Claims parseToken(String token) {
    return Jwts.parser()
    .setSigningKey(secret)
    .parseClaimsJws(token)
    .getBody();
    }
     
    /**
    * 从令牌中获取用户名
    *
    * @param token 令牌
    * @return 用户名
    */
    private String getUsernameFromToken(String token) {
    Claims claims = parseToken(token);
    return claims.getSubject();
    }
     
    /**
    * 获取请求token
    *
    * @return token
    */
    private String getTokenFromRequest(HttpServletRequest request) {
    String token = request.getHeader(header);
    if (StrUtil.isNotEmpty(token) && token.startsWith(Token.PREFIX)) {
    token = StrUtil.stripIgnoreCase(token, Token.PREFIX, null);
    }
    return token;
    }

    File: agileboot-admin/src/main/java/com/agileboot/admin/customize/config/SecurityConfig.java (L34-114)

    /**
    * 主要配置登录流程逻辑涉及以下几个类
    * @see UserDetailsServiceImpl#loadUserByUsername 用于登录流程通过用户名加载用户
    * @see this#unauthorizedHandler() 用于用户未授权或登录失败处理
    * @see this#logOutSuccessHandler 用于退出登录成功后的逻辑
    * @see JwtAuthenticationTokenFilter#doFilter token的校验和刷新
    * @see LoginService#login 登录逻辑
    * @author valarchie
    */
    @Configuration
    @EnableWebSecurity
    @EnableGlobalMethodSecurity(prePostEnabled = true)
    @RequiredArgsConstructor
    public class SecurityConfig {
     
    private final TokenService tokenService;
     
    private final RedisCacheService redisCache;
     
    /**
    * token认证过滤器
    */
    private final JwtAuthenticationTokenFilter jwtTokenFilter;
     
    private final UserDetailsService userDetailsService;
     
    /**
    * 跨域过滤器
    */
    private final CorsFilter corsFilter;
     
     
    /**
    * 登录异常处理类
    * 用户未登陆的话 在这个Bean中处理
    */
    @Bean
    public AuthenticationEntryPoint unauthorizedHandler() {
    return (request, response, exception) -> {
    ResponseDTO<Object> responseDTO = ResponseDTO.fail(
    new ApiException(Client.COMMON_NO_AUTHORIZATION, request.getRequestURI())
    );
    ServletHolderUtil.renderString(response, JSONUtil.toJsonStr(responseDTO));
    };
    }
     
     
    /**
    * 退出成功处理类 返回成功
    * 在SecurityConfig类当中 定义了/logout 路径对应处理逻辑
    */
    @Bean
    public LogoutSuccessHandler logOutSuccessHandler() {
    return (request, response, authentication) -> {
    SystemLoginUser loginUser = tokenService.getLoginUser(request);
    if (loginUser != null) {
    String userName = loginUser.getUsername();
    // 删除用户缓存记录
    redisCache.loginUserCache.delete(loginUser.getCachedKey());
    // 记录用户退出日志
    ThreadPoolManager.execute(AsyncTaskFactory.loginInfoTask(
    userName, LoginStatusEnum.LOGOUT, LoginStatusEnum.LOGOUT.description()));
    }
    ServletHolderUtil.renderString(response, JSONUtil.toJsonStr(ResponseDTO.ok()));
    };
    }
     
    /**
    * 强散列哈希加密实现
    */
    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
    return new BCryptPasswordEncoder();
    }
     
     
    /**
    * 鉴权管理类
    * @see UserDetailsServiceImpl#loadUserByUsername
    */
    @Bean
  • CompreFace 免费开源人脸识别系统

    CompreFace 产品详细介绍

    CompreFace 是 Exadel 开发的免费开源人脸识别系统,是一个基于 Docker 的应用程序,可以作为独立服务器使用或部署在云端。 

    核心特性

    CompreFace 相比其他免费人脸识别解决方案具有多项技术优势: 

    ● 支持 CPU 和 GPU:易于扩展,支持不同硬件配置

    ● 开源和自托管:提供额外的数据安全保障

    ● 灵活部署:可在云端或本地部署

    ● 无需机器学习专业知识:设置和使用简单

    ● 先进技术:使用 FaceNet 和 InsightFace 等最先进的人脸识别方法

    ● 快速部署:只需一条 Docker 命令即可启动

    系统架构

    CompreFace 采用微服务架构,包含以下核心组件: 

    组件容器名称描述
    NGINX 代理compreface-fe提供 UI 服务并作为主网关
    管理服务器compreface-adminSpring Boot 应用,负责 UI 操作
    API 服务器compreface-api处理所有用户 API 调用
    嵌入服务器compreface-core运行神经网络,计算人脸嵌入
    PostgreSQL 数据库compreface-postgres-db存储应用数据

    人脸服务

    CompreFace 提供三种主要的人脸服务: 

    1. 人脸检测服务

    用于检测图像中的所有人脸,但不进行识别。适用于:

    ● 收集商店访客的性别、年龄统计数据

    ● 获取地标信息分析客户关注点

    ● 监控口罩佩戴情况

    2. 人脸识别服务

    用于人脸识别,需要先上传已知人脸到集合中,然后在未知人脸中识别。适用于:

    ● 使用员工照片识别办公室中的陌生人

    ● 根据预注册照片跟踪会议参与者

    ● 在人群中快速找到 VIP 客人

    3. 人脸验证服务

    用于比较两张人脸是否属于同一个人,返回相似度分数。适用于:

    ● 验证客户与其身份证或驾驶证是否匹配

    ● 验证用户连接社交网络账户时的身份

    人脸插件

    CompreFace 提供多种插件来获取额外的人脸信息: 

    ● age:返回推测的年龄范围

    ● gender:返回推测的性别

    ● landmarks:返回面部地标(5个点)

    ● calculator:返回人脸嵌入

    ● pose:返回头部姿态信息

    ● mask:检测是否佩戴口罩及佩戴是否正确

    ● landmarks2d106:返回106个面部地标(仅限 InsightFace 配置)

    API 概述

    CompreFace 提供全面的 REST API,支持: 

    ● 人脸识别 API:管理主题、识别和验证人脸

    ● 人脸检测 API:检测人脸并应用插件

    ● 人脸验证 API:比较两张人脸

    API 支持多种输入格式,包括 multipart/form-data 和 Base64。 

    部署选项

    CompreFace 提供三种部署方式: 

    部署方式优势劣势适用场景
    Docker Compose(默认)配置简单、运行简单、通过 QA 测试需要 Docker Compose、单机运行本地安装
    Kubernetes易于扩展需要 Kubernetes 集群生产环境安装
    单个 Docker 容器配置简单、运行简单可靠性最低、单机运行Docker Compose 不支持时的本地安装

    自定义构建

    CompreFace 提供多种自定义构建以优化不同硬件配置: 

    ● Default:FaceNet + MTCNN,无 GPU 支持

    ● Mobilenet:移动设备优化模型,高性能

    ● ArcFace-r100:InsightFace ArcFace + RetinaFace,高精度

    ● GPU 版本:支持 GPU 加速,性能更高

    集成选项

    CompreFace 提供官方 SDK 简化集成: 

    ● JavaScript SDK:https://github.com/exadel-inc/compreface-javascript-sdk

    ● Python SDK:https://github.com/exadel-inc/compreface-python-sdk

    ● .NET SDK:https://github.com/exadel-inc/compreface-net-sdk

    配置选项

    CompreFace 可通过 .env 文件中的环境变量进行配置,包括: 

    ● 数据库凭据

    ● 邮件服务器设置

    ● 图像存储选项

    ● 性能调优参数

    ● 最大文件大小和请求大小

    Notes

    CompreFace 是一个功能全面的人脸识别系统,采用微服务架构设计,支持多种部署方式和硬件配置。系统无需机器学习专业知识即可使用,提供了丰富的 API 和 SDK 选项,便于集成到各种应用中。所有组件都经过容器化处理,简化了部署和维护过程。

  • Mudler / LocalAI 一个全能的 AI 网关

    Mudler / LocalAI 实际上涉及到一个开源项目的名称及其核心开发者。通常情况下,LocalAI 是指那个开源产品,而 Mudler 则是该项目的创始人和主要维护者(Ettore Di Giacinto)在 GitHub 和技术社区的昵称。

    以下是关于 LocalAI 及其背景的详细介绍:

    1. 什么是 LocalAI?

    LocalAI 是一个旨在替代 OpenAI API 的开源项目。它的核心目标是让你能够在本地运行大语言模型(LLM),同时保持与 OpenAI API 规范的兼容性。

    简单来说,你可以把它看作是一个“本地版的 OpenAI 接口服务”。核心功能与特点:

    ● OpenAI API 兼容性:这是 LocalAI 最大的卖点。它模仿了 OpenAI 的 API 结构(如 /v1/chat/completions, /v1/completions 等端点)。这意味着,如果你正在使用基于 OpenAI API 开发的应用(如 ChatGPT-Next-Web、LangChain 等),你只需要修改 API 的 Base URL 指向 LocalAI,即可无缝切换到本地模型,无需修改代码逻辑。

    ● 无需 GPU(最初设计):虽然它也支持 GPU 加速,但 LocalAI 最初的设计理念是利用 C++ 后端(如 llama.cpp、ggllm.cpp 等)进行推理,这意味着它可以在消费级 CPU、甚至树莓派等边缘设备上运行模型,不需要昂贵的高端显卡。

    ● 支持多种模型格式:它不仅支持 LLM(如 Llama 2、Mistral、GPT4ALL 等),还通过后端支持音频转文字(Whisper)和图像生成。

    ● 模型库:LocalAI 提供了一个模型库功能,用户可以通过简单的配置文件自动下载和安装模型,降低了使用门槛。

    2. 关于 “Mudler” (开发者)

    Mudler 是 LocalAI 项目的核心开发者。他在 GitHub 上非常活跃,致力于推动本地 AI 生态的发展。

    ● 他的贡献主要集中在简化复杂 AI 工具的部署流程,让开发者不需要深入了解底层推理引擎(如 ggml 或 llama.cpp 的具体细节),就能通过 API 调用模型。

    ● 除了 LocalAI,Mudler 还参与了其他相关的 AI 基础设施项目,旨在推广去中心化和隐私保护的 AI 解决方案。

    3. LocalAI 的应用场景

    ● 隐私敏感环境:数据不需要上传到云端,完全在本地处理,适合企业内部机密文档处理或个人隐私保护。

    ● 离线环境:在没有互联网连接的环境下(如实验室、内网环境)提供 AI 能力。

    ● 成本控制:一旦模型下载完成,运行时不产生 Token 费用,且不依赖昂贵的云服务。

    ● 应用开发与测试:开发者在测试 AI 应用时,可以使用免费的本地模型进行调试,而不必消耗 OpenAI 的额度。

    4. 总结

    Mudler 是这个开源项目的“大脑”,而 LocalAI 是他的核心作品。

    如果你想在本地搭建一个类似 ChatGPT 的服务,并且希望它能兼容现有的 OpenAI 生态系统,LocalAI 是一个非常优秀的选择。它通过容器化技术,使得部署一个大模型后端变得像运行一个 Web 服务一样简单。

    希望这个介绍能帮你理解这两个名词!如果你有关于如何部署或使用 LocalAI 的具体问题,也欢迎继续问我。

  • Blakeblackshear/Frigate本地实时视频对象检测系统

    Blakeblackshear/Frigate是一款开源的本地实时视频对象检测系统,专为IP摄像头设计,核心基于OpenCV与TensorFlow实现低延迟分析,支持Docker部署并可与HomeAssistant等平台集成,适合本地部署以保障数据隐私与控制。以下从核心特性、技术架构、部署与集成、应用场景及版本更新等方面详细介绍:

    核心定位与优势

    ● 本地优先:所有视频分析在本地完成,不依赖云端,兼顾低延迟(毫秒级)与数据隐私,尤其适合注重隐私的家庭或小型商业场景。

    ● 轻量高效:通过Coral Edge TPU等硬件加速,大幅降低CPU占用,支持多摄像头24/7录制与实时检测并行。

    ● 智能检测:内置对象检测(人、车、宠物等)、运动触发录制、区域划分与自定义告警规则,减少误报。

    ● 开放集成:原生支持MQTT与HomeAssistant,可接入自动化流程(如联动灯光、门锁),并提供WebUI与API便于二次开发。

    技术架构与工作流

    1.  视频采集:通过FFmpeg或go2rtc接入RTSP/RTMP等主流摄像头流,支持主码流(高分辨率录制)与子码流(低分辨率检测)分离,平衡性能与画质。

    2.  运动检测:先通过帧差法快速定位运动区域,缩小后续AI分析范围,提升效率。

    3.  AI分析: 基础检测:使用TensorFlow Lite+MobileNet/YOLO等模型,支持自定义标签与置信度阈值。

    a.  分类增强(v0.17+):新增状态分类(如门开关)与对象细分类(如特定宠物、是否戴头盔),支持本地训练MobileNetV2模型。

    b.  硬件加速:优先适配Coral TPU,也支持CPU/GPU推理,满足不同硬件配置。

    4.  事件处理:触发条件(如区域内出现人)时生成快照、短视频,通过MQTT推送告警,并支持按规则过滤误报。

    5.  存储与回放:支持本地磁盘/网络存储,提供WebUI按事件检索与回放,支持权限管理(v0.17+新增自定义视图角色)。

    部署与集成方式

    部署方式适用场景核心步骤
    Docker容器通用Linux/Windows/macOS拉取镜像 → 配置config.yml → 挂载设备(TPU/摄像头)→ 启动容器
    HomeAssistant插件智能家居用户插件商店安装 → 配置MQTT → 关联摄像头与自动化规则
    源码编译二次开发/定制安装依赖(OpenCV/TensorFlow)→ 编译 → 配置加速硬件

    ● 关键配置:config.yml支持摄像头参数、检测区域、对象类型、告警规则、存储路径等,可通过WebUI在线修改。

    ● 集成生态: MQTT:推送检测结果、控制指令至HomeAssistant/Node-RED等平台。

    ○ HomeAssistant:提供传感器(帧率/检测数)、二进制传感器(对象存在)、摄像头实时视图与媒体浏览器。

    ○ 第三方工具:支持Frigate Hass Card(可视化卡片)、Webhook与API对接自定义系统。

    版本与功能演进(关键更新)

    ● v0.8.0:支持24/7录制与运动触发结合,提升实用性。

    ● v0.15.0:架构优化(Breaking Change),增强稳定性与硬件兼容性。

    ● v0.17.0(beta):新增分类模型、自定义视图角色、事件摘要生成,扩展AI能力与权限控制。

    ● 持续迭代:社区活跃,定期更新模型、修复漏洞,支持自定义模型训练与插件扩展。

    应用场景与价值

    1.  家庭安防:本地检测+实时告警,联动智能家居(如开门开灯、远程查看),保护隐私不泄露视频流。

    2.  小型商业:门店/仓库监控,检测异常人员、车辆停留,生成事件日志便于复盘,降低云端成本。

    3.  工业/园区:通过分类模型检测合规性(如安全帽佩戴),区域入侵告警,适配本地部署需求。

    4.  二次开发:开发者可基于开源代码定制检测逻辑,如接入声音识别、与数字人系统联动,拓展场景边界。

    关键资源

    ● 代码仓库:https://github.com/blakeblackshear/frigate

    ● 官方文档:https://docs.frigate.video

    ● Docker镜像:blakeblackshear/frigate

    ● 社区支持:HomeAssistant论坛、GitHub Issues、Discord频道

    选型建议

    ● 适合人群:注重隐私的家庭用户、小型商户、智能家居爱好者、需要本地AI分析的开发者。

    ● 硬件推荐:入门级(CPU推理,1-2路摄像头);进阶(Coral USB/PCIe TPU,4-8路摄像头);企业级(多TPU集群,16+路摄像头)。

    ● 对比云端方案:优势是隐私可控、无订阅费、低延迟;劣势是需自行维护硬件与配置,AI能力依赖本地模型。

  • Onlook是一个面向前端开发者的可视化开发工具

    Onlook 是一个开源的、面向前端开发者的可视化开发工具。它的核心理念是填补“设计”与“代码”之间的鸿沟,让开发者能够在浏览器中像使用设计软件一样直接编辑 React/Next.js 应用的界面,并自动生成高质量的代码。

    简单来说,你可以把它理解为“运行在你本地代码上的 Figma”

    以下是 Onlook 的详细介绍:1. 核心定位

    Onlook 主要解决的是前端开发中繁琐的 UI 调整过程。通常,开发者需要在 IDE(如 VS Code)中修改代码,刷新浏览器查看效果,反复调试。而 Onlook 允许你直接在浏览器中选中元素进行拖拽、修改样式(颜色、间距、字体等),这些更改会自动且同步地写回到你本地的项目代码中。

    2. 主要功能特性

    ● 本地优先

    ●  与 Webflow 或 Figma 这种基于云端画布的工具不同,Onlook 直接运行在你本地的开发环境上。你编辑的就是你真实的 React 组件,所见即所得。可视化编辑

    ●  提供类似 Figma 的操作体验。你可以点击页面上的任何元素,通过右侧面板修改 CSS 属性,或者直接拖拽调整布局。智能代码生成

    ●  它不是生成一堆难以维护的 div,而是尝试理解你现有的代码结构(React 组件、Tailwind CSS 等),并生成整洁、语义化的代码。Figma 导入与同步

    ●  支持(或计划深度支持)从 Figma 导入设计稿,并将其转换为 React 代码,帮助设计师与开发者更好地协作。基于现有技术栈

    它目前主要支持 React 和 Next.js,并且对 Tailwind CSS 有很好的支持,这符合现代主流的前端开发栈。3. 工作原理

    1.  安装:通过 npm 在你的项目中安装 Onlook 的 CLI 工具。

    2.  运行:启动 Onlook,它会启动一个本地代理,并与你的 Next.js 开发服务器进行交互。

    3.  编辑:打开 Onlook 提供的界面(通常是一个侧边栏或覆盖层),在浏览器中选中元素进行修改。

    4.  同步:当你保存更改时,Onlook 会直接修改你磁盘上的源代码文件(如 .tsx 或 .css 文件)。

    4. 适用人群

    ● 前端开发者:希望快速构建 UI 原型,或者厌倦了反复调整 CSS 像素值的开发者。

    ● 独立开发者:需要一个人同时完成设计和 coding,希望提高效率的人。

    ● 全栈工程师:专注于后端逻辑,希望在前端 UI 上花费最少时间的人。

    5. 总结

    Onlook 的目标是让 UI 开发变得像设计一样简单,同时保持代码的纯净和可维护性。它并不是要取代 IDE,而是作为 IDE 的强力辅助插件,将“视觉调整”这一环节从手写代码转变为可视化操作。

    如果你对它感兴趣,可以在 GitHub 上搜索 onlook-dev/onlook 查看其源码和文档。