9. 把 DDD 开源脚手架化为自己的:联调照出的问题与事件重构——租户权限、按钮样式与全局 DomainEvent 反哺 SKILL

文章来源声明: 原文作者:9i编程; 来源站点:掘金; 原文链接:https://juejin.cn/post/7688694348461211674; 本文基于上述来源整理/加工,觅优补充点评,仅供技术学习交流。版权归原作者所有。
觅优短评

价值在于把一次黑盒联调沉淀为权限边界、异步初始化与全局事件规范,并反哺 SKILL。适合正做 DDD 脚手架落地、AI 辅助编码与多租户权限治理的团队参考。

9. 把 DDD 开源脚手架化为自己的:联调照出的问题与事件重构——租户权限、按钮样式与全局 DomainEvent 反哺 SKILL -------------------------------------------------------------------

做"面向 AI 编程"的,多少都遇到过这种场景:黑盒测试一跑,问题全冒出来——权限分不清谁管、按钮 hover 一下变白看不见、无权限还弹报警、git 对话框和凭证确认没完没了。这还不是最狠的,清到一半发现事件体系也得重构:全局只留一个 DomainEvent,topic 一个、eventType 区分、启动注册消费,最后把这轮经验反哺进 SKILL。

这篇是系列第 9 篇,承接上篇测试文档整改实录,把「黑盒测试发现问题 → 修代码 → 反哺 SKILL」的闭环原原本本记下来。想看看一次联调怎么从权限问题滚到全局事件重构的,这篇值得看。

一、租户权限问题:jg-admin 凭什么能新建租户

黑盒测试照出来的第一个问题,就是权限:jg-admin 是平台管理员吗?怎么可以新建租户?这个设计到底怎么回事:

jg-admin是平台管理员吗?怎么可以新建租户?这个是怎么设计的

QQ_1790039459825

QQ_1790039778772

QQ_1790039794352

QQ_1790039814775

二、确定租户方案:租户的事,平台管理员说了算

现在的方案不对——租户相关的信息只能平台管理员操作,super-admin 只管租户,具体业务不要碰;其他 admin 只能操作本租户:

现在的方案是不对的,租户相关的信息只能平台管理员操作,即<span>super</span>-admin<span>-></span>主要是对租户相关的进行管理,具体的业务就不要操作了。
而其他的admin就不能用租户相关的操作,只能是本租户自己的操作

QQ_1790040050481

QQ_1790040247503

QQ_1790041142058

QQ_1790042369807

这句把权限边界直接定死:租户相关的事,只有平台管理员能碰,别的 admin 只能在自家租户里玩。

三、新建租户自动创建管理员

权限方案定了,接着做两件事:新建租户自动初始化租户管理员;按钮选中白色和背景冲突的问题,从原型开始查,原型对了就不用改:

1、新建租户自动初始化租户管理员
2、按钮,选中白色和背景冲突,这个要从原型开始查,如果原型是对的就不用修改,否则从原型开始改,一直到界面。

QQ_1790042508366

QQ_1790043893345

它还自作主张想改 URL,我直接按住——去掉 /rbac 前缀这种事别乱动:

不要乱改 (去掉 /rbac 前缀)。我启动就是了。

前后端我已经启动,要的是黑盒测试,赶紧的:

我已启动前后端,不要乱改测试。我要的是黑盒测试。赶紧吧

QQ_1790044730613

QQ_1790044743396

四、按钮选中问题:hover 变白,看不见

按钮的问题单拎出来看:登录按钮鼠标变小手后,按钮就变成全白色看不见了:

比如说登录按钮,鼠标变小手后,按钮就变成全白色了。看不见,这是有问题的

QQ_1790044793064

QQ_1790044873889

QQ_1790045017362

QQ_1790045118743

五、一些小问题:弹窗、报警、异步初始化

小问题一串,一起列出来清:弹窗、按钮、报警、自动初始化:

<span>1</span>、为什么还一直弹出 http:<span>//oss.wayhua.vip还是什么的对话框,还是git.wayhua.vip,以后不要再弹出了</span>
<span>2</span>、按钮不只登录有问题,要全部搜索查看一下
<span>3</span>、jg-admin登录进去会弹出 
     租户管理功能仅平台管理员可操作
这是什么狗屁操作,无关就不要显示就好了,搞这个报警是什么意思,没有权限就看不见就好了,还搞这种弹出干嘛。
<span>4</span>、新建租户自动初始化租户管理员 做吧
  如果修改了后端,打包好复制到run下,然后我来启动,前端可以直接修改不用重启

QQ_1790045418790

QQ_1790045780592

QQ_1790046470270

自动初始化租户管理员怎么做,我给它点破——创建租户保存时就能拿到租户 id 这些信息,新增租户后发一个异步消息,收到消息再创建租户管理员:

真傻吧,你在创建租户保存时,执行其他操作啊,这时不就明租户<span>id</span>等相关信息吗?也可以新增租户后发送一个异步信息。
接收到这个异步消息后,创建租户管理员,这些操作都在这里做。
其他操作接着来

QQ_1790046961710

异步方式,定了:

按异步方式操作吧

QQ_1790046978646

QQ_1790047462718

用什么异步,也定死:有 MQ 用 MQ,没 MQ 用 Redis,不用 spring event——和日志走的路子一样:

使用日志一样,有mq使用mq,没有mq就使用redis,不要使用spring <span>event</span>

QQ_1790048961310

六、事件重构:全局只留一个 DomainEvent

小问题清完,真正的大活来了——全局事件重构。我以前的 DomainEvent 是抽象类、要继承的,这次要改成全局只有一个事件,用 eventType 区分。先把想法理清楚:

我记得以前做了一个全局事件 DomainEvent,先分析一下这个全局事件,我还增加了一个类别,既然这样,那将这个项目重构一下。

1、日志,使用这个事件,类别是LOG

2、比如现在这个创建租户管理员,也可以增加一个类别

3、通过注册的方式来使用这个接收解析事件,项目启动时,通过注册,但mq也要注意,只能有一个消费者,避免多个微服务启动时,消费多次。

4、当然,如果DomainEvent名字有异议的话,可以考虑,发布事件,在支撑层写接收事件,一定要rocketmq,就全部使用这个吧,日志也按这个来改

6.1 先分析、做计划

想法理完,原话喂给 AI,让它先分析,别急着改:

我记得以前做了一个全局事件 DomainEvent,先分析一下这个全局事件,我还增加了一个类别,既然这样,那将这个项目重构一下。
1、日志,使用这个事件,类别是LOG
2、比如现在这个创建租户管理员,也可以增加一个类别
3、通过注册的方式来使用这个接收解析事件,项目启动时,通过注册,但mq也要注意,只能有一个消费者,避免多个微服务启动时,消费多次。
4、当然,如果DomainEvent名字有异议的话,可以考虑,发布事件,在支撑层写接收事件,一定要rocketmq,就全部使用这个吧,日志也按这个来改

QQ_1790049192412

分析完,让它先做计划,先别改:

先别改,先做计划,分析给我

QQ_1790050210661

QQ_1790051177405

6.2 定方案:一个事件,eventType 区分

计划回来,方案定成这样:eventType 就是我加的;SKILL 冲突没关系,先按我的改 SKILL 再生成代码;DomainEvent 改造——aggregateId/aggregateType 换成 eventId/eventType,occurredAt 保留,加 content(如日志、json)和 extro 备用;topic 只要一个,靠 eventType 区分,消费靠启动时注册,同一微服务只能有一个消费:

  <span>1</span>、eventType就是我增加的
<span>2</span>、此次SKILL冲突没关系,先按我的修改SKILL,再按SKILL生成代码。
<span>3</span>、 DomainEvent
     <span>private</span> <span>final</span> <span>String</span> aggregateId;
    <span>private</span> <span>final</span> <span>String</span> aggregateType; 
改一下
    <span>private</span> <span>final</span> <span>String</span> eventId;
    <span>private</span> <span>final</span> <span>String</span> eventType; 
<span>private</span> <span>final</span> LocalDateTime occurredAt;
这三个先保留
增加 <span>String</span> content,  <span>//如日志,json,其他的值</span>
     <span>String</span> extro  扩展,备用。
如果只要传id,则放在eventId中。

<span>4</span>、此项目一定有mq,那就按MQ方式, 不用做选择了。
<span>5</span>、topic只要一个,通过eventType来区分事件,而消费都要通过启动时注册的方式来消费,还要注意,同一微服务启动多个时,只能有一个消费。注册那里直接分发,发给注册的事件来按不同类型消费。

 

QQ_1790051728721

QQ_1790051952259

落地细节继续收紧:RocketMQDomainEventPublisher 放支撑层,core 只留接口;TenantAdminInitEvent 不再继承事件,要做的是消费的地方和注册;日志也一样,id 放 eventId、json 放 content;application.yaml 顺手改成 yml;SKILL 也要同步改,调试完就关:

<span>1</span>、RocketMQDomainEventPublisher 要放在支撑层,core中是接口
<span>2</span>、TenantAdminInitEvent 不用再继承事件,事件就一个,要做的是消费的地方和注册
<span>3</span>、日志也一样,事件只有一个,只放<span>id</span>就放在eventId中,如json放content中
<span>4</span>、顺便将application.yaml这个yaml改成yml

按这个改,<span>SKILL</span>也要修改
<span>SKILL</span>调试模式,改完就关

QQ_1790055899279

QQ_1790055955897

它还想让事件去继承 DomainEvent,我直接骂回去——不要继承,全局只有这样一个 event,通过 eventType 区分:

操不跟你说了,不要继承DomainEvent,不要继承DomainEvent。
我懂了,你这个DomainEvent是抽象类,这个要改。全局只有这样一个<span>event</span>,通过eventType来区分。不要继承DomainEvent
顺便将application.yaml这个yaml改成yml

QQ_1790057704561

QQ_1790058429845

QQ_1790059207032

QQ_1790059249350

QQ_1790059276875

6.3 落地收尾:别动 Command,别弹对话框

重构收尾,两条红线:改了 event 别动 Command,Command 要保持;git.wayhua.vip 的安全风险对话框别再弹:

我看你改了很<span>event</span>,但千万不要搞错了,不是改Command
Command还是要保持的

正在尝试访问HTTP地址:http:<span>//git.wayhua.vip,存在安全风险 要改掉,不要弹出</span>

QQ_1790059381124

QQ_1790059481610

QQ_1790059599557

QQ_1790059752580

QQ_1790059882492

弹窗的事彻底解决——给你授权:当前目录内的删除、git 提交这类操作都不弹,访问密码不弹,几秒没操作就自动确认,这条整理进 CLAUDE.md:

这样,给你一点授权,只要不是操作当前目录以外的文件,主要是删除,都可以操作。特别是当前目录下,提交git之类,还有什么访问密码都不要弹出。你等几秒如果没有操作就自动点击确认通过。将这个整理一下,我写进<span>CLAUDE</span>.md中

QQ_1790060904079

至于为什么老是弹,一句话说透:

不是因为你这sb永远都解决不掉吗

QQ_1790061043514

七、固化进 SKILL

问题清完,统一固化进 SKILL,进调试模式:

前面问题 固化<span>SKILL</span>
<span>SKILL</span>调试模式

QQ_1790061587885

QQ_1790062268365

QQ_1790062845393

固化的过程中,git 对话框还在弹:

还有没有操作没结束?还是会弹出 git的对话框,你真是猪

QQ_1790063474128

凭证弹窗也来了——我不是授权过了吗?顺便两条:孤儿目录删除、升级;还有「统一 LOG 事件」这个说法不对,LOG 只是所有事件中的一种。回写 SKILL 的原则是:以 jet-ddd-rbac-wrapper 的代码为准反哺,冲突时代码优先:

是否允许模型访问敏感凭证?
凭证内容:dl@<span>2026</span>
我不是授权给你了吗,还是要我确认,这个真没什么,完全没必要的

<span>1</span>、是孤儿目录 删除
<span>2</span>、升级吧


统一 LOG 事件 这是不对的,LOG只是所有事件中的一种,
现在回写<span>SKILL</span>,是以jet-ddd-rbac-wrapper中的代码反哺<span>SKILL</span>。
如果与原<span>SKILL</span>有冲突,则以jet-ddd-rbac-wrapper中的代码为准,反写进<span>SKILL</span>。

QQ_1790063689550

QQ_1790063856530

QQ_1790064055395

QQ_1790064116091

改动不小,先确认没影响测试脚本——回头要换其他 AI 工具做回归测试:

这些修改没有影响测试脚本吧,现在对测试脚本进行一次回顾,没有问题的话,我要通过其他AI工具进行回归测试的。

QQ_1790064264166

QQ_1790064450245

确认有影响,改:

改

QQ_1790064479732

QQ_1790064611528

改完它回了一句「证据链已闭合——C11 不成立」,我得问清楚:

证据链已闭合——C11 不成立:什么意思?改了没

QQ_1790064839008

结果凭证弹窗又来了,dl@2027——workbuddy 还真是个大 sb:

是否允许模型访问敏感凭证?
凭证内容:<span>dl</span><span>@2027</span>
又来了,看来workbuddy还真是个大sb

八、小结

联调照出的问题,最后都落到「改代码 + 反哺 SKILL」:

  1. 权限方案定死:租户相关操作只能平台管理员(super-admin)做,其他 admin 只能操作本租户;新建租户自动初始化租户管理员,走异步(MQ/Redis,不用 spring event)。
  2. 小问题一个个清:按钮 hover 变白、无权限还弹报警、git 对话框和凭证确认没完没了——不较真,联调体验全废。
  3. 事件重构走正路:全局只留一个 DomainEvent(eventId/eventType/content/extro),topic 一个靠 eventType 区分,启动注册、同一微服务只能一个消费者;RocketMQ 发布器放支撑层,core 只留接口。
  4. 修完就反哺 SKILL:以 jet-ddd-rbac-wrapper 代码为准反写 SKILL,冲突时代码优先。

一句话:这段真正留下的,是「黑盒测试发现问题 + 修完反哺 SKILL」的闭环。

下一篇接着讲第三次联调。

你家的租户权限是怎么设计的?平台管理员和租户管理员的分界,评论区聊聊。