DebateLab-个人博客(1)后端总体架构与比赛状态机设计
在这一篇博客中我打算首先把后端整体的框架搭建好后端是单体 Spring Boot 应用首先确定下来了项目整体目录安排由于本次项目涉及到了许多板块和业务项目内容量较大如果只是按照单个的controller或者service类来区分的话会导致多业务混在一起不够清晰所以我依据不同的业务层面拆分到不同文件夹不同业务按auth / topic / debate / ai / config / common拆模块持久层用MyBatis Mapper XML实现数据库用 MySQL。最终目录如下本阶段目标这一阶段我先解决两个问题完成后端框架的设计包括springboot框架搭建项目目录设计以及jwt登录功能的实现。后端不能只是实现聊天接口必须能控制整场比赛怎么开始、怎么推进、什么时候轮到用户、什么时候进入评分和复盘。后端必须给出一个明确、可查询、可继续推进的比赛状态。本阶段完成内容这一阶段我把后端主骨架搭起来了重点在三个点。第一按业务拆模块而不是把所有controller/service/mapper平铺在一起。现在的职责边界比较清晰auth负责注册、登录、JWT 鉴权topic负责论题包和辩位模板debate负责比赛流程、日志、评分、复盘ai负责 Java 和 Python 服务之间的调用第二把比赛抽象成状态机。核心阶段放在DebateStep里不是随便用一个字符串记当前阶段而是把顺序、阶段名、发言辩位一起固定下来。像下面这样publicenumDebateStep{PREPARE(1,PREPARE,赛前准备,null),AFFIRMATIVE_OPENING(2,OPENING,正方一辩立论,DebateRole.AFFIRMATIVE_1),NEGATIVE_OPENING(3,OPENING,反方一辩立论,DebateRole.NEGATIVE_1),AFFIRMATIVE_CROSS_EXAMINATION(4,CROSS_EXAMINATION,正方二辩攻辩/质询,DebateRole.AFFIRMATIVE_2),NEGATIVE_CROSS_EXAMINATION(5,CROSS_EXAMINATION,反方二辩攻辩/质询,DebateRole.NEGATIVE_2),AFFIRMATIVE_REBUTTAL(6,REBUTTAL,正方三辩驳论推进,DebateRole.AFFIRMATIVE_3),NEGATIVE_REBUTTAL(7,REBUTTAL,反方三辩驳论推进,DebateRole.NEGATIVE_3),AFFIRMATIVE_FREE_DEBATE_MAIN(8,CONTROLLED_FREE_DEBATE,正方三辩受控自由辩主攻,DebateRole.AFFIRMATIVE_3),NEGATIVE_FREE_DEBATE_MAIN(9,CONTROLLED_FREE_DEBATE,反方三辩受控自由辩主攻,DebateRole.NEGATIVE_3),AFFIRMATIVE_FREE_DEBATE_SUPPORT(10,CONTROLLED_FREE_DEBATE,正方二辩受控自由辩补刀,DebateRole.AFFIRMATIVE_2),NEGATIVE_FREE_DEBATE_SUPPORT(11,CONTROLLED_FREE_DEBATE,反方二辩受控自由辩补刀,DebateRole.NEGATIVE_2),AFFIRMATIVE_CLOSING(12,CLOSING,正方四辩总结陈词,DebateRole.AFFIRMATIVE_4),NEGATIVE_CLOSING(13,CLOSING,反方四辩总结陈词,DebateRole.NEGATIVE_4),JUDGING(14,JUDGING,裁判评分,null),REVIEW(15,REVIEW,赛后复盘,null),COMPLETED(16,COMPLETED,比赛完成,null);}之所以选择状态机设计流程是因为辩论流程天然具有阶段性和顺序依赖。后端通过 currentStep、status、currentTurnIndex 和 userActionRequired 这些状态字段统一约束比赛从赛前准备、各辩位发言到评分复盘的流转。这样可以避免流程判断散落在前端和接口里也更方便做日志回放、异常重试和后续扩展。这样后端也天然知道三件事当前进行到哪一步这一步有没有发言人下一步应该推进到哪第三把“是否需要用户输入”也做成后端控制逻辑。MatchService在推进比赛时会根据当前step和userRole计算userActionRequired然后通过MatchDetailResponse返回给前端。前端只需要根据返回结果决定显示“继续推进”还是“提交发言”。当前可展示结果目前后端已经能跑通的功能用户注册 / 登录获取论题列表创建比赛对应的swagger接口截图比赛详情接口返回的数据也不是简单的一段文本而是结构化状态像这样{currentStep:NEGATIVE_OPENING,currentPhase:OPENING,currentSpeakerRole:NEGATIVE_1,userActionRequired:false,canAdvance:true,status:IN_PROGRESS,currentTurnIndex:1,availableActions:[ADVANCE,RETRY_CURRENT_STEP]}遇到的问题与修复在流程设计的过程中一开始我不太确定自由辩的流程应该如何进行设计不知道受控自由辩论具体要从什么层面“受控”想了几种方案比如限制对话字数、限制对话轮数或者是由agent自己进行判定但是最后感觉这些方案都不太现实不方便设计而且并没有很强的实际意义。最后我决定设计成正/反方的二/三辩各自轮流发言一次这样既可以实现一定程度的自由发挥又能保证流程的可控性不至于设计太异想天开。下一阶段安排架构和状态机骨架定下来之后下一步就该把数据库表设计落地这样就可以稳定保存对局、日志和结果数据。之后我准备重点介绍表结构建模是怎么定下来的。