admin-bff 接口全面就绪 + 前端功能规划定稿 + 安全统一

今日工作 1. admin-bff 接口全面检查与补齐 前端功能规划定稿后,逐一比对前端目录树与 admin-bff 实际接口,发现 1 处缺失(商品图片接口),已补齐。 补齐内容: ProductPhotoFeignClient(新建)→ mall-product-client AdminProductExtraController 新增 productPhoto 5 个 CRUD 接口 最终 admin-bff 共 36 个接口,覆盖前端全部页面。这是前端开发可以直接对着写的接口清单。 2. 前端功能规划定稿 docs/30-admin前端功能规划.md 经过多轮讨论最终定稿。核心原则: 页面按角色可见: 角色 可见页面 超级管理员 全部(系统管理/商品/订单/营销/基础数据/评价) 运营部 商品管理/营销/首页/基础数据/评价 客服部 订单管理/评价 财务部 订单管理(只读金额) 不开发的前端页面: 菜单管理、角色管理、部门管理、岗位管理、字典管理、定时任务——这些由开发维护 DB,不出现在前端。 权限关系简化为:部门 + 岗位 → 角色 → 菜单,超级管理员只需要在用户管理里选部门/岗位,权限自动带出。 3. RBAC 数据初始化 向 cloud_mall_admin 数据库写入预设数据: 部门:运营部、客服部、财务部 角色:超级管理员(已有)、运营(ops)、客服(service)、财务(finance) 岗位表(auth_job)待预设,后续补上。 4. 商品上下架字段补全 发现 product 表没有上下架字段——整个商品系统没有上架/下架的概念。已修复: ALTER TABLE product ADD COLUMN status tinyint(1) DEFAULT 1 COMMENT '上下架状态 1:上架 0:下架'; ProductEntity 同步新增 status 字段,文档补充商品列表支持按状态筛选。 ...

七月 3, 2023 · 1 分钟 · 200 字 · yaomingye

今日日报:common-web 大重构 + R4 FallbackFactory 自动配置 + 技术枚举落地

今日工作 1. common-web 目录整理 + 注解化控制 common-web 之前的目录有点乱, @EnableXxx 注解和 @Configuration 配置类全混在 config/ 包里。今天拆了一刀: 改前: config/ EnableApiResultWrapper.java ← 注解 EnableRequestLogFilter.java ← 注解 ApiResultWrapperConfiguration.java RequestLogFilterConfiguration.java WebAutoConfiguration.java 改后: annotation/ ← 注解单独放 EnableApiResultWrapper.java EnableRequestLogFilter.java config/ ← 只放 @Configuration ApiResultWrapperConfiguration.java RequestLogFilterConfiguration.java WebAutoConfiguration.java 同时引入了 @EnableXxx 模式替代原本的 @ConditionalOnProperty : @EnableRequestLogFilter — 替代 3 个服务里重复的 RequestLogFilterConfig.java ( FilterRegistrationBean 配置完全相同,只是包名不同) @EnableApiResultWrapper — 控制 GlobalApiResultHandler 是否生效 2. GlobalApiResultHandler 失效之谜 GlobalApiResultHandler 实现了 ResponseBodyAdvice<Object> ,通过 WebAutoConfiguration#@Bean 注册。按理说 Spring MVC 会自动发现,但实际上没生效——所有 @RestController 的返回都是裸数据,没有被 ApiResult 包装。 ...

七月 1, 2023 · 3 分钟 · 477 字 · yaomingye

今日日报:Nacos 配置统一收敛 + FeignFallbackProxy 降级代理重构 + Resilience4j 落地

今日工作 1. FeignFallbackProxy:用动态代理干掉模板代码 昨天写了 8 个 FallbackFactory,每个 50+ 行,全是 return null / 空列表 / 0 的模板代码。今天抽了个 FeignFallbackProxy 到 common-core,基于 JDK 动态代理,根据方法返回类型自动推断兜底值。 // 改前:14 个方法逐个手写 return new UserFeignClient() { @Override public List<UserDTO> findByIds(List<Long> ids) { log.warn("[降级] findByIds 返回空列表"); return Collections.emptyList(); } // ... 每个方法都来一遍 }; // 改后:一行搞定 return FeignFallbackProxy.create(UserFeignClient.class, cause); 8 个工厂从 ~400 行缩到 ~40 行,核心逻辑全部收敛到 FeignFallbackProxy 一处。以后加新的 FeignClient 降级也只需要 5 行。 2. Nacos 配置统一收敛 之前每个服务在 Nacos 里都有一份独立的 yml,Redis 连接配了 8 遍、JWT 密钥配了 11 遍。花了半天把 common.yaml 重新扶正——所有共享配置(Redis、JWT、devtools、bean-override、Resilience4j)归到 common,各服务只留数据库、中间件等特有配置。 ...

六月 29, 2023 · 1 分钟 · 167 字 · yaomingye

今日日报:Admin 控制器合并、Swagger 描述优化、Feign 熔断降级体系搭建

今日工作 1. Admin 控制器合并重构 mall-admin 的 controller 层从 15 个砍到了 7 个。 删了 8 个——4 个 internal 控制器(UserInternalController、RoleInternalController、DeptInternalController、JobInternalController),4 个关联表控制器(UserRoleController、RoleMenuController、RoleDeptController、UserAvatarController)。 理由很简单:internal 控制器的方法本来就是对同一张表查数据,合并到 UserController、RoleController、JobController 就够了。关联表控制器纯 CRUD,也没人调,留它过年。 UserFeignClient 的请求路径也顺手从 /v1/internal/user/* 改到了 /v1/auth/user/*,因为 internal 路径已经不存在了。 2. Swagger 描述统一 之前 @Operation(summary = "通过id查询")、@Operation(description = "删除") 这种写了等于没写。 这次给 6 个 Controller 的每个接口都加了三段式描述——认证要求 + 参数说明 + 业务说明: 需 Bearer Token + admin 角色 | 查询参数:id(用户ID) 无需认证(公开接口)| 无参,返回全部角色列表 需 Bearer Token | 请求体:RoleConditionEntity(分页条件) 顺便把白名单逻辑的现状写进了 Security 注释,哪些接口免登录一目了然。 3. Feign 熔断降级体系 这是今天的大头。项目之前 Feign 调用异常处理几乎裸奔——FeignResultDecoder 的核心逻辑被人注释掉了,没有 ErrorDecoder、没有 FallbackFactory、没有断路器。 ...

六月 27, 2023 · 1 分钟 · 166 字 · yaomingye

全链路修复:微服务 JWT 鉴权踩坑记

今日工作 1. Nacos 配置 namespace 发现与修复 项目里 12 个微服务全部使用 Nacos 作为配置中心和注册中心,namespace 是 7af60364-xxx-xxx-xxx 的自定义空间,但之前某次推送配置时没指定 namespace,全部落到了 public 下。等于写了半天的配置全白干。 问题: 服务启动时就报数据库连接超时、Redis 连不上——因为在 public 命名空间下根本找不到 mall-admin-api-dev.yaml 这类配置。 修复: 确认各服务本地 application.yml 中的 spring.cloud.nacos.config.namespace 已正确指向目标命名空间。由用户在 Nacos 控制台手动导回配置。 同时发现 3 个服务(gateway、admin、customer)在本地 application.yml 中通过 shared-configs 引用了 common.yaml ,但该文件早已名存实亡、内容冲突(同一个 key 在 YAML 里出现两次,如 spring: 和 management: 各两遍)。直接移除 shared-configs 引用。 2. MyBatis mapper XML 加载失败 admin 服务调用 POST /v1/internal/user/testLogin 返回 {"code":1,"message":"用户名或密码错误"} ,但密码哈希确认没问题,数据库也查得到用户。查日志发现原因并非密码不匹配,而是 MyBatis mapper XML 文件根本未被加载。 // UserMapper 接口 package cn.net.mall.admin.mapper.auth; // 接口在 auth 包 // UserMapper.xml(XML 文件) // 实际路径: .../mapper/admin/UserMapper.xml // 文件在 admin 目录 接口包名是 auth ,XML 文件路径是 admin ——MyBatis 默认的 mapper-locations 是 classpath*:mapper/**/*.xml ,根本扫不到 cn/net/mall/... 路径下的文件。所有 mapper 方法调用都静默抛异常,被 testLogin 的 catch 块兜底包装为"用户名或密码错误",极具迷惑性。 ...

六月 25, 2023 · 4 分钟 · 772 字 · yaomingye

合并与修复:mall-common重构、Nacos恢复、中间件排查

今日工作 1. 外部 Starter 合并回 mall-common 之前将 mall-common 的基础设施拆分到了独立仓库 mall-spring-boot-starters (含 common-core 、 redis-starter 、 workid-starter 、 web-starter 、 sensitive-starter ),但独立维护成本高、构建链长、改个工具类要跨仓库发版。今日全部合并回主项目。 具体操作: 5 个 external starter 模块搬入主项目,改用主项目 parent POM mall-common-core 与 mall-common 去重(删除 14 个重复工具类,由 common-core 提供) Redisson 依赖彻底移除(零处使用,纯历史包袱) JJWT 统一为 0.12.6 拆分 artifacts,替代旧版合并 JAR 各服务 POM 去掉外部 starter 引用,改为本地模块依赖 2. Application 启动类命名统一 去除 Api 后缀,统一为 {模块名}Application (如 BasicApiApplication → BasicApplication );BFF 层加 Bff 后缀( AdminApiApplication → AdminBffApplication )。 影响范围: 8 个文件改名 + 对应 SpringApplication.run() 引用修复。 ...

六月 23, 2023 · 2 分钟 · 218 字 · yaomingye

Maven Surefire 测试中的 MalformedInputException:当 JVM 默认编码与 Nacos 配置编码不一致时

又见编码问题:mvn test 报 MalformedInputException,IDE 却好好的 某开发者最近在项目中遇到一个奇怪的现象:mvn test 跑 Spring Boot 集成测试时,应用上下文启动失败,控制台抛出一串 MalformedInputException: Input length = 1。但同样的代码,在 IDE 里直接点"运行"按钮,启动得丝般顺滑。 这看起来像是 Nacos 配置中心的问题——因为错误信息里提到了 nacos:mall-auth-api-dev.yaml 配置文件找不到。但用 curl 请求 Nacos API,配置明明存在,内容也是合法的 YAML。 到底是哪里出了问题? 问题复现 执行命令: mvn test -pl mall-auth 控制台输出类似: *************************** APPLICATION FAILED TO START *************************** Description: Config data resource 'NacosConfigDataResource{...}' via location 'nacos:mall-auth-api-dev.yaml' does not exist 但此刻如果打开浏览器访问 Nacos 控制台,或者用 curl 直接拉取: curl "http://localhost:8848/nacos/v1/cs/configs?dataId=..." 配置内容完整返回,HTTP 状态码 200。所以配置是存在的,但 Nacos 客户端在 Spring Boot 中读取失败了。 翻到堆栈深处,真正的异常是: Caused by: org.yaml.snakeyaml.error.YAMLException: java.nio.charset.MalformedInputException: Input length = 1 at com.alibaba.cloud.nacos.parser.NacosDataParserHandler.parseNacosData(...) at com.alibaba.cloud.nacos.configdata.NacosConfigDataLoader.pullConfig(...) Caused by: java.nio.charset.MalformedInputException: Input length = 1 at java.base/java.nio.charset.CoderResult.throwException(...) at java.base/sun.nio.cs.StreamDecoder.implRead(...) 这就清楚了——不是配置"不存在",而是配置内容解析时遇到了字符编码问题。SnakeYAML 在读取 YAML 时,接收到了一个无法解码的字节序列。 ...

六月 19, 2023 · 4 分钟 · 726 字 · yaomingye

微服务文档重构:API 分组、BFF 聚合与文档拆迁

今天干了啥:分组、聚合、拆文档 今天没写啥牛逼的业务代码,大部分时间在跟文档和接口结构较劲。记个流水账。 一、发现 doc.html 不对劲 打开一个微服务的 Knife4j 文档页,下拉框里赫然列着七八个其他服务的分组。一点就 404,明摆着是隔壁服务跑这儿串门了。 第一反应是 knife4j 的配置问题,加了一堆 enableXxx: false ,重启——纹丝不动。后来发现 knife4j 基础 starter 压根没有跨服务聚合功能。真正的原因藏在 SpringDoc 的配置链路里,某些共享配置文件往 swagger-config 端点的 urls 字段里塞了外部的分组 URL。 最后在每个服务的本地 application.yml 把 springdoc 配置全写死,用本地覆盖干掉了注入。顺便把相关的配置说明写进系统设计文档了。 产出: 一篇踩坑博客 + 配置修复。 二、给接口文档分了三个层 之前所有微服务的接口都在一个组里,前端要看、后端也要看、微服务 Feign 调用也混在一起。这次给每个服务分了三个 Swagger 分组: 前端接口(给前端的) 后台接口(给管理后台的) 内部接口(给其他微服务调用的) 每个服务各自独立,不再相互干扰。 三个分组的接口在代码上也做了物理隔离——新建了 controller/internal/ 包,只给微服务间 Feign 调用用。顺便把原来跟前端接口冲突的方法也挪过去了,URL 统一加 /v1/internal/ 前缀,从根上避免路由冲突。 三、把前端需要的接口聚合到 BFF 层 之前前端写个页面经常要调好几个微服务,商品详情页调了 5 次接口。虽然 BFF 层之前就已经做了一些聚合(首页聚合、商品详情聚合、下单预览聚合),但管理后台这边基本还是透传状态。 给管理后台 BFF 加了两个聚合接口: 用户编辑页:一次查出用户信息 + 角色列表 + 部门树 + 岗位列表 商品编辑页:一次查出商品详情 + 分类树(品牌和单位的数据等后续补 Feign 客户端) 同时给两个 BFF 服务都加上了 Swagger 文档。以后前端重写,只看这两个 BFF 的文档就够了,不用再翻 8 个微服务的接口。 ...

三月 16, 2023 · 1 分钟 · 127 字 · yaomingye

Knife4j 文档聚合:微服务里那些「不请自来」的 API 分组

隔壁服务的 API,怎么跑我这儿来了? 某天启动 auth 服务,打开 http://localhost:8021/doc.html ,想看一眼自己刚调好的三个 API 分组——等等,下拉框里怎么还有 message、order 的分组?点过去全是 404,auth 服务上根本就没有这些接口。 flowchart LR subgraph USER["👤 开发者"] A(["打开 auth 的\ndoc.html"]) end subgraph ACTUAL["期望"] B([只显示自己\n3 个分组]) end subgraph REAL["现实"] C([显示了 8 个\n不同服务的分组]) end A --> B A --> C C --> D{other groups\ntap 404} D -->|yes| E[「这就很烦了」] 代码是同一套代码,springdoc 和 knife4j 版本都是统一管理的,为什么 auth 的文档页面里会出现其他服务的痕迹?某开发者决定,今天不修好不下班。 第一反应:去 knife4j 找配置 这种"在一个服务里看到另一个服务的 API",第一感觉就是 knife4j 的网关聚合功能 在作祟。毕竟 knife4j 有个专门的 knife4j-aggregation-spring-boot-starter ,专门用来在 gateway 上聚合所有微服务的文档。 二话不说,给每个服务的 application.yml 加上: knife4j: enableAggregation: false 重启,刷新——没变化,其他分组稳如泰山地挂在下拉框里。 ...

三月 14, 2023 · 3 分钟 · 607 字 · yaomingye

Spring Cloud 微服务接入 BFF 聚合层:从一个混乱的项目重构说起

当你的单体项目被拆成微服务,前端第一个崩溃 某天接手了一个从老单体拆出来的微服务电商项目。技术栈倒是很"大厂"——Spring Cloud Alibaba、Nacos、Sentinel、RocketMQ、ShardingSphere,你能想到的全塞上了。 但前端同事过来敲门的时候,事情就不太对劲了。 “咱这项目一共几个文档地址?” “9 个。"(每个后端服务一个 Knife4j 页面) “那我要调一个登录接口,该看哪个服务的文档?” “……好问题。” 这就是典型的微服务拆了,但没完全拆——后端确实拆成了 9 个独立服务,可前端仍然需要知道每个服务的地址、每个接口的路径、每个返回的字段含义。而且很多接口其实需要前端自己拼数据:登录完了再查一遍用户信息、再查一遍菜单权限、再查一遍角色列表。 前端不是在写业务,是在做 API 聚合。 BFF:不是新概念,但能解决真问题 BFF(Backend For Frontend)的核心思路很简单:每个前端都有一个专属的后端入口,这个入口干三件事: 聚合 — 把多个后端服务的数据合并成前端需要的一站式响应 裁剪 — 只返回前端真正需要的字段,不裸奔整个数据库实体 隔离 — 后端再怎么拆、再怎么重构,前端代码不用动 架构上看起来就是中间多了一层: flowchart LR subgraph CLIENT["📱 前端"] WEB(["管理后台 Web"]) APP(["移动端 小程序"]) end subgraph GW["🚪 网关层"] GATEWAY[Spring Cloud Gateway\nJWT · CORS · Sentinel] end subgraph BFF["🎯 BFF 聚合层"] ADMIN_BFF["mall-admin-api\n管理后台 BFF\n端口 8090"] MOBILE_BFF["mall-mobile-api\n移动端 BFF\n端口 8091"] end subgraph BACKEND["⚙️ 业务微服务"] AUTH[mall-auth-api] BASIC[mall-basic-api] PRODUCT[mall-product-api] ORDER[mall-order-api] MARKETING[mall-marketing-api] OTHERS[其余 4 个服务...] end WEB -->|"/api/admin/**"| GATEWAY APP -->|"/api/mobile/**"| GATEWAY GATEWAY --> ADMIN_BFF GATEWAY --> MOBILE_BFF ADMIN_BFF -->|Feign 调用| BACKEND MOBILE_BFF -->|Feign 调用| BACKEND classDef bffFill fill:#2e1065,stroke:#a855f7,stroke-width:2.5px,color:#f8fafc,font-weight:bold; class ADMIN_BFF,MOBILE_BFF bffFill; 为什么要加这一层?直接转发不行吗? 接手时项目就已经有两个"BFF 模块"了—— mall-mobile-api 和 mall-admin-api 。但打开一看,里面就一个 ForwardController ,用 RestTemplate + LoadBalancerClient 把所有请求原封不动转发到后端服务: ...

三月 12, 2023 · 4 分钟 · 739 字 · yaomingye
Cat Radio