博客

  • eigent-ai/eigent 多智能体桌面应用

    Eigent(Eigent AI)是CAMEL – AI团队开发的100%开源多智能体桌面应用,能构建、管理和部署定制化AI劳动力,将复杂多步骤工作流自动化,在GAIA基准测试中表现突出,且支持本地部署,数据隐私性强。以下从核心定位、架构、功能、优势、应用场景等方面详细介绍:

    核心定位

    Eigent是一款专注于复杂工作流自动化的桌面应用,区别于单智能体系统的局限,通过多智能体并行协作,为专业人士和高级用户提供更快、更可靠、成本更低的任务处理结果,用户可自定义智能体团队,适配不同业务需求。

    核心架构

    1.  Task Manager(任务管理器):作为系统“大脑”,负责理解用户整体目标,将模糊需求拆解为具体可执行的子任务,并制定整体推进计划。

    2.  Coordinator(协调器):扮演“项目经理”角色,负责调度工作、分配任务、处理任务间依赖关系,汇总所有任务完成后的结果。

    3.  Worker Nodes(工作节点):专注具体操作,如查信息、写代码、处理数据或文档等,多个节点可并行工作,互不干扰。

    4.  Owl协作框架:基于CAMEL – AI构建的多智能体协作框架,在GAIA基准测试中以58.18平均分位列开源框架第一,实现动态智能体交互,提升协作效率。

    核心功能与特性

    特性说明
    多智能体并行协作配备开发者、搜索、文档、多模态等专业化智能体,可并行执行任务,支持三级并行(workforce间、work间、子任务内工具调用),大幅提升效率
    高度自定义支持构建定制化Worker nodes,集成自定义工具,通过MCP协议接入内部API或自定义函数,内置200多个MCP工具,也可自行扩展
    隐私优先与本地部署100%开源,可本地部署,使用自有API密钥或本地LLM,数据与敏感工作流完全可控,不会离开本地环境
    人类介入机制(Human – in – the – Loop)智能体遇困或不确定时,系统自动暂停并请求人工输入,关键决策点人工可介入,保障结果符合预期
    丰富工具集成内置网络浏览、代码执行、Notion集成、Google Suite连接、Slack集成等大量MCP工具,满足多样化场景需求

    核心优势

    1.  效率显著提升:多智能体并行执行任务,相比单线程处理,整体速度提升数倍,三级并行机制进一步提高任务处理效率。

    2.  自由可控:代码开源透明,用户可自由修改、优化,甚至商用,适配不同业务场景的定制化需求。

    3.  安全可靠:本地运行,数据不上传云端,有效避免数据泄露风险,同时人类介入机制可减少错误,保证任务结果可靠。

    4.  适配性强:支持自定义智能体功能、工具扩展和本地LLM集成,可根据项目需求灵活创建AI团队,适配多种复杂工作场景。

    应用场景

    1.  开发者工作流:自动完成代码编写、调试、运行终端命令、文档生成等任务。

    2.  内容创作与调研:智能体分工协作完成资料搜索、数据收集、内容撰写、报告生成等。

    3.  企业业务流程:自动化处理市场调研、数据分析、客户服务等多步骤业务流程,集成Slack、Notion等办公工具提升协作效率。

    4.  个人高效办公:处理邮件分类、日程管理、文件整理等复杂办公任务,减少人工操作。

    部署与获取

    Eigent目前处于公开测试阶段,用户可在GitHub获取源代码,参与测试还能获得额外积分奖励,也可直接下载桌面应用进行体验,部署方式灵活,支持本地部署与云端使用两种模式。

  • NginxPulse轻量级的 Nginx 访问日志分析与可视化面板

    一、一句话概括

    NginxPulse 是一款轻量级的 Nginx 访问日志分析与可视化面板,可以实时统计访问量(PV)、按多种维度过滤日志,并自动解析 IP 归属地和客户端信息,自带 Web 前端,适合部署在个人服务器、中小站点或内部环境中使用。

    二、产品定位与核心价值

    ● 目标场景: 已经使用 Nginx 作为 Web 服务器 / 反向代理,希望通过访问日志做一些「轻量级」实时监控与分析。

    ○ 不想引入重量级监控/可观测平台(比如 ELK、Prometheus+Grafana 等),只想要一个开箱即用的小工具。

    ● 核心价值: 部署简单:Docker 一键跑起来,不需要复杂依赖。

    ○ 日志分析自动化:自动解析 Nginx 访问日志,PV/访问趋势、IP 归属地、客户端信息等全部自动完成。

    ○ 轻量:单容器内集成前端与后端,使用 SQLite 本地存储,不依赖外部数据库服务。

    三、整体工作流程示意

    下面是 NginxPulse 的大致工作流程:

    flowchart TB
      A[Nginx 服务器<br/>access.log] --> B[NginxPulse 后端<br/>定时扫描日志]
      B --> C[日志解析<br/>提取 IP / URL / 状态码 / UA 等]
      C --> D[IP 归属地查询<br/>ip2region + ip-api.com]
      C --> E[写入 SQLite 数据库]
      E --> F[后端 API 提供<br/>统计与过滤查询]
      F --> G[前端可视化面板<br/>图表与多维筛选]
      G --> H[运维/开发人员<br/>查看访问情况与异常]
    

     四、主要功能特性

    1) 日志分析与实时统计

    ● 定时扫描你指定的 Nginx 访问日志文件,解析其中的字段(如 IP、时间、URL、状态码、User-Agent 等)。

    ● 将解析后的数据写入本地 SQLite 数据库,供后续查询与可视化使用。

    ● 支持按配置的时间间隔(TASK_INTERVAL)来定时扫描,默认为 1 分钟。

    2) PV 统计与灵活过滤

    ● PV 统计可按指定 HTTP 状态码来统计(默认只统计 200 成功请求,也可自定义状态码列表)。

    ● URL 排除规则:支持配置 PV_EXCLUDE_PATTERNS(URL 正则数组),比如排除静态资源、爬虫路径等,避免污染 PV 统计。

    ● IP 排除规则:支持配置 PV_EXCLUDE_IPS,将指定 IP(如内部监控、健康检查)从统计中排除。

    ● Referer 与站内访问:通过 WEBSITES 中配置 domains,可以把来自指定域名的 referer 归类为「站内访问」,方便区分外部来源。

    3) IP 归属地解析(多级策略)

    NginxPulse 为 IP 归属地设计了一套“快速 + 准确”的多级策略:

    ● 快速过滤: 空值 / 本地 / 回环地址直接返回“本地”。

    ○ 内网地址直接返回“内网/本地网络”。

    ● 缓存优先: 内存缓存,最多缓存 50,000 条 IP 查询结果,重复 IP 直接命中缓存,减少外部 API 调用。

    ● 远程优先: 调用 ip-api.com/batch 批量接口查询归属地,超时时间 1.2s,每批最多 100 个 IP。

    ● 本地兜底: 当远程失败或者结果为“未知”时,IPv4 使用内置的 ip2region 数据库做本地查询,超时 50ms。

    ○ 本地数据库文件 ip2region.xdb 内嵌在二进制中,首次启动会自动解压到 ./var/nginxpulse_data/ip2region.xdb,并加载向量索引加速查询。

    ● IPv6 处理: IPv6 仅走远程 API,远程失败则返回“未知”。

    ● 注意: 项目会访问外网 IP 归属地 API(ip-api.com),部署环境需要放行该域名的出站访问。

    4) 客户端解析

    ● 基于 HTTP 请求中的 User-Agent 等字段,识别客户端类型(浏览器、爬虫等)与基础设备信息(具体展示形式以前端页面为准)。

    5) 可视化面板(Web UI)

    ● 前端技术栈:Vue 3 + Vite + TypeScript + PrimeVue + ECharts/Chart.js + Scss,使用组件化 UI 和图表库,便于做交互式展示。

    ● 提供在线演示:nginx-pulse.kaisir.cn(可直接访问体验可视化效果)。

    ● 常见可视化能力包括(以前端实际页面为准): 访问量趋势图(按时间维度统计 PV)。

    ○ IP 访问分布(国家/地区分布、常见来源 IP)。

    ○ URL 热度排行(哪些路径被访问最多)。

    ○ 状态码分布(2xx/3xx/4xx/5xx 比例)。

    ○ 客户端/浏览器类型统计。

    6) 多站点/多日志支持

    ● WEBSITES 支持传入数组,每个元素包含: name:站点名称。

    ○ logPath:容器内日志路径。

    ○ domains(可选):与该站点关联的域名列表,用于 referer 站内访问分析。

    ● 通过挂载多个日志文件或整个日志目录,并在 WEBSITES 中配置多条记录,即可用一个 NginxPulse 实例同时分析多个站点或多组日志。

    7) 演示模式(Demo Mode)

    ● 提供 DEMO_MODE 环境变量(默认 false),开启后: 定时生成模拟日志,并直接写入数据库。

    ○ 不再解析真实日志文件,适合演示、测试 UI 和功能,而不需要准备真实 Nginx 日志。

    五、技术架构与组件

    ● 后端: 语言:Go 1.23.x。

    ○ Web 框架:Gin。

    ○ 日志库:Logrus。

    ○ 数据存储:SQLite(使用 modernc.org/sqlite)。

    ● 前端: 框架:Vue 3。

    ○ 构建工具:Vite。

    ○ 语言:TypeScript。

    ○ UI 组件:PrimeVue。

    ○ 图表:ECharts / Chart.js。

    ○ 样式:Scss。

    ● 容器与部署: 单容器镜像内包含: 后端服务(Go)。

    ■ 前端静态资源(由 Nginx 提供访问)。

    ○ 支持 Docker / Docker Compose 一键部署与编排。

    六、部署与使用方式概览

    1) 使用 Docker Hub 远程镜像(最简单)

    ● 示例命令(来自 README):

    docker run -d --name nginxpulse \
    -p 8088:8088 \
    -p 8089:8089 \
    -e WEBSITES='[{"name":"主站","logPath":"/share/log/nginx/access.log","domains":["kaisir.cn","www.kaisir.cn"]}]' \
    -v ./nginx_data/logs/all/access.log:/share/log/nginx/access.log:ro \
    -v "$(pwd)/var/nginxpulse_data:/app/var/nginxpulse_data \
    magiccoders/nginxpulse:latest

    ● 访问: 前端:http://localhost:8088

    ○ 后端 API:http://localhost:8089

    2) Docker Compose 部署

    ● 远程镜像版本的 docker-compose.yml 示例:

    version: "3.8"
    services:
    nginxpulse:
    image: magiccoders/nginxpulse:latest
    container_name: nginxpulse
    ports:
    - "8088:8088"
    - "8089:8089"
    environment:
    WEBSITES: '[{"name":"主站","logPath":"/share/log/nginx/access.log","domains":["kaisir.cn","www.kaisir.cn"]}]'
    volumes:
    - ./nginx_data/logs/all/access.log:/share/log/nginx/access.log:ro
    - ./var/nginxpulse_data:/app/var/nginxpulse_data
    - /etc/localtime:/etc/localtime:ro
    restart: unless-stopped

    ● 启动:

    docker compose up -d

    3) 手动构建与开发

    ● 前端构建:

    cd webapp
    npm install
    npm run build

    ● 后端构建:

    go mod download
    go build -o bin/nginxpulse ./cmd/nginxpulse/main.go

    ● 本地开发(前后端一起跑):

    ./scripts/dev_local.sh

    ● 开发环境说明: 前端开发服务默认 8088,并把 /api 代理到 http://127.0.0.1:8089。

    ○ 本地开发前准备好日志文件(如放在 var/log/ 下,或修改配置中的 logPath)。

    七、关键配置说明(环境变量)

    主要环境变量与含义(无配置文件时尤为关键):

    ● WEBSITES(必填): JSON 数组,每个元素为站点对象,字段: name:站点名称。

    ■ logPath:容器内日志路径。

    ■ domains(可选):站点域名列表,用于站内访问统计。

    ● CONFIG_JSON(可选): 完整配置 JSON 字符串(等同 configs/nginxpulse_config.json)。

    ○ 设置后会忽略本地配置文件,其他环境变量仍可覆盖其中字段。

    ● LOG_DEST(可选,默认:file): 日志输出位置:file 或 stdout。

    ● TASK_INTERVAL(可选,默认:1m): 扫描间隔,支持 Go duration 格式(如 5m、25s)。

    ● DEMO_MODE(可选,默认:false): 开启演示模式,生成模拟日志。

    ● SERVER_PORT(可选,默认::8089): 后端监听地址,可传 :8089 或 8089。

    ● PV_STATUS_CODES(可选,默认:[200]): 统计 PV 的状态码列表,支持 JSON 数组或逗号分隔。

    ● PV_EXCLUDE_PATTERNS(可选,默认有内置规则): 全局 URL 排除正则数组(JSON 数组)。

    ● PV_EXCLUDE_IPS(可选,默认为空或使用配置文件): 排除 IP 列表(JSON 数组或逗号分隔)。

    八、多日志与多站点实战建议

    ● 方式一:逐条挂载多个日志文件: WEBSITES 数组中为每个网站配置一个 logPath,volumes 分别挂载对应日志文件(只读)。

    ● 方式二:挂载日志目录,然后在 WEBSITES 里按需指定文件: 示例:volumes 挂载 ./nginx_data/logs:/share/log/nginx。

    ○ WEBSITES 中 logPath 写为 /share/log/nginx/access-site1.log、access-site2.log 等。

    ● 挂载 var/nginxpulse_data: 用于持久化 SQLite 数据库和解析缓存,推荐保留以便重启不丢数据。

    九、适用场景与限制

    ● 适合: 个人博客、中小型站点、内部系统访问分析。

    ○ 想「从 0 到 1」快速搭建一套日志可视化面板,而不是搭建完整可观测平台。

    ● 限制: 面向 Nginx 访问日志,主要做访问统计与可视化,不提供全面的 APM/链路追踪能力。

    ○ 使用单机 SQLite,适合中小规模;如果日志量特别大或需要多节点协同分析,需要评估或扩展。

    ○ 使用外部 IP API(ip-api.com),内网环境需要保证能访问该域名,或者自行更换/禁用远程查询功能。

    十、如何开始?

    如果你现在就想试用:

    ● 最快方式: 使用上面的 Docker 命令或 docker-compose.yml,把你的 Nginx access.log 路径映射进去。

    ○ 设置好 WEBSITES,然后访问 http://your-host:8088 查看面板。

    ● 想先看效果: 直接打开演示站点:nginx-pulse.kaisir.cn 体验 UI 与交互。

    十一、开源与许可

    ● 项目在 GitHub 上开源(likaia/nginxpulse),采用 MIT 许可证,可自由使用、修改和分发。

  • 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 的具体问题,也欢迎继续问我。