ops
|
d57615c8d5
|
fix(ci): 构建容器名唯一化 + npm 走国内镜像源(避免并发互踩 exit 137)
|
2026-10-21 06:24:32 +00:00 |
|
ops
|
868a9f1bc3
|
ci: apk 源统一走 aliyun(规避 dl-cdn 时段性被拦卡 60s)
|
2026-10-21 01:37:04 +00:00 |
|
lijing
|
53c08e10e6
|
feat(表单): 接收班级按「年级」分组下发,对齐家校活动模块页
build / build-push (push) Successful in 1m48s
- ToolParam 新增 groupByParent(),FormField 新增 groupByParent 字段;
未配 optionsFrom 却声明分组时 toFormField() 直接抛异常
(内置选项是平铺的,没有父节点,留成死配置最难排查)
- activity.publish 的 receiveList 声明 groupByParent,对齐
FamilyActivity/src/views/activity/NewActivity.vue 的 OptionGroup 年级分组
- ModuleFormParityTest 新增 2 条:
activityReceiveListGroupsOptionsByGrade、
groupByParentRequiresADictionaryThatCanReturnATree(含「没配 optionsFrom 必须抛」的反例)
背景:班级接口返回的是「年级 → 班级」两层树,但助手表单原先把树拍平成叶子,
下拉里只剩一排「一班、二班」,同名班级分不清年级。
验证:mvn -o test 145/145 BUILD SUCCESS
|
2026-10-15 17:00:33 +08:00 |
|
lijing
|
50f6fa8f75
|
feat(表单): 发布表单支持「条件显示」,宿舍通知/家校活动按模块页口径联动
build / build-push (push) Successful in 53s
- ToolParam.visibleWhen(依赖字段, 取值…) + FormField.visibleWhen:字段随上级字段取值增删;
前端 FormCard.isFieldVisible() 统一管渲染 / 必填校验 / 提交体过滤(只判断显示,不清值)。
- ToolParam.requiredWhenVisible():条件必填,只约束界面这一次填写,不进给模型的 schema
—— 否则模型会在「不需要」的场景硬凑一个值出来。
- ToolParam.hiddenValue(值):隐藏时提交体里仍带固定值。两种隐藏口径必须区分:
· 宿舍通知「回执单据」= `: undefined` ⇒ 隐藏即整键不发,不声明 hiddenValue;
· 家校活动「名额上限/家长随行」= `? 0 : …` ⇒ 控件隐藏但照发 0,声明 hiddenValue(0)。
没配 visibleWhen 却给了 hiddenValue 直接抛异常(恒显示的字段上它是死配置)。
- dorm.publish:formType 仅 receiptMode=fill 显示且条件必填;needSupervisor 在 confirm|fill 显示。
- activity.publish:quota / allowAccompany 在 signup|confirm 显示,隐藏时提交 0;说明词同步。
- 契约测试 +2:活动联动(含「与宿舍口径必须相反」的反向断言)、
hiddenValue 必须搭配 visibleWhen 且取值须是字段自己的合法选项。
|
2026-10-15 15:47:28 +08:00 |
|
lijing
|
006003c842
|
feat(表单): 五个模块正文换成模块页同款富文本,公告/宿舍的接收对象换成自绘控件
build / build-push (push) Successful in 1m15s
彻底对齐各模块「新建 / 发布」页里的两类控件(此前只有通知公告搬到位):
正文 → wangEditor(widget=richtext)
· 校园公告 content、宿舍通知 content(发布 + 编辑)、家校活动 content(发布 + 编辑)、
作业 description
· 教师专业发展 remark:只声明形态、**不声明** .richText(...) —— 该模块 file 域只有
Upload/Preview/Down(没有 UploadImage),模块页 RecordEdit.vue 也没配 uploadImage,
所以下发空地址,前端据此把工具栏的「图片」组整组摘掉(模块页就是这个形态)
接收对象 → 自绘控件(widget=scopepicker / roompicker)
· 校园公告 pushScopes:换成 ClassScopeInput 同款(全部班级 / 指定班级 + 弹窗班级树);
提交口径不变,仍是 [{scopeType,scopeId,scopeName}]
· 宿舍通知 receiveList:换成模块页「选择接收宿舍」弹窗同款(左楼栋 / 右按楼层分组的房间);
v-model 是字符串(房间 id 逗号串或 ALL 哨兵),与模块 receiveList 的真实契约一致
基建
· ToolParam 新增 richText(upload,preview) / scopePicker(type) / roomPicker() /
pickerSource(页签名, 模块内相对路径);对应 FormField 四个字段
· AgentOrchestrator:新增 resolveModulePath 通用形态,richtext 与 pickerSources 的地址
一律只写模块内相对路径、前缀在这里拼;richtext **没有兜底**(不声明就是空)
· 带 optionCall 的字段会被改写成 select 的白名单改用 isSelfDrawnPicker()
(原先是硬编码 userpicker),并放开为 public 供契约测试复用生产口径
测试(139 tests, 0 failures)
· ModuleFormParityTest 新增两条通用不变量:正文图片地址与弹窗数据源都必须带模块前缀、
不许自己带前缀、不许双斜杠;另加「专业发展不得拿到兜底上传地址」
· FormFieldWidgetTest 新增 richtext(含「不声明 = 空」反向断言)与自绘控件透传两条
· IntentDirectFormTest / DormActivityPublishIntentTest 同步改成新控件口径
|
2026-10-14 16:56:07 +08:00 |
|
lijing
|
c084c978aa
|
feat(通知公告): 发布表单支持模块页的富文本正文与选人控件
build / build-push (push) Successful in 2m1s
前端已把 NoticeFront 的 wangEditor 与自绘「通知对象」控件 1:1 搬进助手表单,
后端补上这两个控件所需的契约:
- ToolParam / FormField 新增:
· pickerSources:选人弹窗的**数据源页签**(形如 {name, 模块内相对地址}),
由 AgentOrchestrator 拼上模块前缀后下发(沿用「只写相对路径」的铁律);
· richTextUploadPath / richTextPreviewPath:正文插图的上传与预览地址。
- AgentOrchestrator:把 resolveUploadPath 抽出通用形态 resolveModulePath(幂等、只接受
模块内相对路径);buildForm 为 widget=userpicker 下发 pickerSources;
optionCall 强制改写 widget 的白名单放行 userpicker(否则会被降级成 select)。
- NoticeTools:notify.publish 与 notify.editNotice 的 receiveList 改为 userpicker,
两个数据源为「基础平台」/api/base/GetSchoolAll 与「自定义组」
/api/contacts/GetGroupTree?type=1;content 走 richtext。
测试:NotifyIntentFormTest / IntentDirectFormTest 补 pickerSources 断言
(**只写相对路径**,不带前缀 —— 前缀拼错是本项目反复出过的坑),
ModuleFormParityTest 补 userpicker 取值口径断言(字符串原样透传,不得包成数组)。
mvn -o test:Tests run: 135, Failures: 0, Errors: 0,BUILD SUCCESS。
|
2026-10-14 15:19:24 +08:00 |
|
lijing
|
8f0f8a04b3
|
feat(intent): 「我要发布通知公告」裸意图直达(通知公告模块补齐)
build / build-push (push) Successful in 57s
通知公告(key=notice、前缀 /notice)此前只有 mock 里有裸意图分支,
真实后端 IntentShortcut 缺这一条,前后端行为不一致。
- IntentShortcut: 新增 NOTIFY_PUBLISH 意图 + NOTIFY_PUBLISH_BARE 短语集
★ 短语一律带「通知公告」全称限定 —— 它与「校园公告」(campusnotice)
是两个独立应用(按用户下发 vs 按班级下发),光说「发通知/发布通知」
两边都成立,猜错模块比不直达更糟;「发通知」继续保持不直达。
- 新增 NotifyIntentFormTest(7):规则识别 / 与校园公告的边界 /
「发通知」不劫持 / 发布工具为写操作且仅教职工 / 代取地址带 /notice 前缀。
- IntentDirectFormTest:补「发布通知公告」端到端两条(教职工直出表单、
学生落回模型),并断言表单里不出现校园公告独有字段。
- DormActivityPublishIntentTest:四个发布意图互不串味(补 notify)。
mvn -o test: 135/135 BUILD SUCCESS
|
2026-10-14 12:40:46 +08:00 |
|
lijing
|
9410599008
|
feat(通知公告): 接入 NoticeFront(模块 notice、工具前缀 notify.*)
build / build-push (push) Successful in 1m7s
★「通知公告」与既有「校园公告」是两个应用,不是同一个:
· 校园公告 CampusAnnouncement → key=campusnotice、前缀 /campusnotice、按班级下发、带审批流/草稿箱(既有 notice.*)
· 通知公告 NoticeFront → key=notice、前缀 /notice、按用户下发,无审批/草稿/推送范围(本次新增 notify.*)
两者路由与页面名几乎同名(/list、/list/new、已收到/已发送),极易拿错源码,故工具前缀避开已占用的 notice.*。
新增 NoticeTools(9 个工具):收件箱/详情/一键已读、发布/编辑/我发出的/阅读情况、发文部门字典、组织树。
发布表单逐项对齐 NoticeFront/src/views/notice/NewNotice.vue#doAdd:
title / content / deptId / receiveList / fileIds / timingSendTime
(smsFlag 在页面里没有对应控件、恒为 0,故不放出;receiveList 的「全部用户」哨兵是 all。)
配套:ToolRegistry 注册、RolePolicy 把发布闭环与两个字典工具列为教职工专属、
application.yml 补地址 http://base.yunhaixingtong.com/notice 与默认落地页 /
(AgentSettings 出厂默认表也要补 notice —— 契约测试用的是 new AgentSettings(),不读 yml)。
fix(ToolExecutor): 已按 joinSeparator 拼成逗号串的字段不再被包回数组
normalizeJsonArgs 的第二个循环「数组参数给了标量 → 包成单元素数组」会作用到
刚拼好的 receiveList/fileIds 上,把 101,102 变成 [101,102],
而模块侧 NoticeRequest.receiveList 是 String ⇒ 直接判非法。
此前一直没暴露:本地 mock 的提交体是原样 JSON.stringify(args)(本来就是数组),
看不出差异,只有真实后端才走到这一步。现在 join 过的字段直接跳过该循环。
测试:ModuleFormParityTest 新增「通知公告发布字段集不与校园公告互串」+
「代取地址带 /notice 前缀」+「提交体是逗号串、哨兵互斥、提交路径带前缀」;全量 126/126 绿。
|
2026-10-14 12:27:31 +08:00 |
|
lijing
|
c3477c6588
|
fix(专业发展): 「发布专业发展」直达表单,所属栏目不再问两遍
build / build-push (push) Successful in 1m27s
用户反馈:说「我要发布专业发展」没直接出表单,先被反问「要选哪个栏目」,
答完出来表单,「所属栏目」却还是空的要再选一次 —— 一件事问了两遍。
两处根因:
1) profession.publish 的说明只写了「调用前必须确认所属栏目 moduleId 与标题」,
漏了公告 / 宿舍 / 活动 / 作业四个发布工具都有的那句「缺失就直接调用本工具,
系统会弹出完整表单让用户填写,不要用文字反复追问」—— 模型读到「必须确认」
就照字面先去口头确认了;
2) 「发布专业发展」不在裸意图直达表里,这句必然要过一轮模型。
改动:
- 工具说明按另外四个发布工具的同款句式补齐(说明词是模型唯一的施工图);
- IntentShortcut 新增「发布专业发展记录」意图(仅教职工),说「我要发布专业发展」
直接弹表单;栏目是学校自建的、各校 id 不同,所以**不预置**,
由用户就在表单里选 —— 这正是「直接出表单选栏目」;
- 契约测试 everyPublishToolTellsTheModelNotToKeepAskingInWords:凡是名字以
.publish 结尾的工具,说明里必须带「不要用文字反复追问」(漏写会被测出来);
- IntentDirectFormTest 补两条:教职工直达出表单(含栏目下拉与代取地址、且不预置
栏目)、学生落回模型。
配套(前端 86e3a10):即便真的先问了一轮再弹表单,对话里定好的值也会被对齐到
选项取值(数字 71 → "71",名称「数学」→ SUB02),表单里不再重复问。
|
2026-10-14 11:35:43 +08:00 |
|
lijing
|
42a7a8030d
|
fix(upload): 附件上传地址统一由编排层拼模块前缀,修专业发展上传 404
build / build-push (push) Successful in 52s
用户在自己线上环境传专业发展附件时接口 404。根因:`ToolParam.upload()` 的覆盖值被当成
**完整路径**原样下发,于是 `profession.publish` 声明 `/api/file/Upload` 就打到站点根
`http://base.yunhaixingtong.com/api/file/Upload` —— 那里没有应用。
(请假那次是「前缀拿模块 key 拼错」,这次是「压根没拼」,同一个坑的两种形态。)
- `AgentOrchestrator` 新增 `resolveUploadPath(module, baseUrl, override)`:
约定与 `ToolDef.path` 完全一致 —— **参数只写模块内相对路径**,模块前缀一律在这里拼;
对「已经带了前缀」的写法保持幂等,调用方不必猜该写哪一种。顺带删掉只剩一处调用的
`modulePathPrefix`,把注释合并到 `pathPrefix` 上。
- `HomeworkTools.UPLOAD_PATH` 改为模块内相对路径 `/api/file/Upload`(下发值不变:
`/coursehomework/api/file/Upload`),与 `ProfessionTools` 统一写法。
- `ToolParam.upload()` 的文档写清约定,避免下一个人再写完整路径。
测试(`ModuleFormParityTest`,替换掉原先「测试自己重算一遍拼接逻辑」的弱断言):
- `everyPublishToolUploadPathMatchesItsModuleRealEndpoint`:逐条钉住 6 个模块的线上真实地址
(对照各模块发布页源码:NoticeUpload.vue / AttachmentUpload.vue / RecordEdit.vue),
其中 profession 那条正是这次线上 404 的回归用例;
- `noUploadPathIsEverEmittedWithoutItsModulePrefix`:**通用不变量** —— 遍历整个注册表,
任何附件字段的最终地址都必须以所属模块前缀开头、不许出现裸 `/api/…` 与双斜杠,
且 `.upload()` 里不许自带前缀。以后新增模块/附件字段会自动被覆盖。
全量 121/121 BUILD SUCCESS。
|
2026-10-14 09:56:21 +08:00 |
|
lijing
|
8cfbb949b6
|
feat(profession): 专业发展发布工具按模块页重写,新增「栏目动态字段」下发机制
build / build-push (push) Successful in 2m15s
对齐 ProfessionView/src/views/profession/RecordEdit.vue:
- profession.publish 字段改为 moduleId/title/grade/subject/ifOpen/remark/contents/fileIds
(POST_FORM /api/profession/Create;附件走 /api/file/Upload;fullForm 全字段渲染)
- 新增 4 个字典工具:profession.grades / subjects / moduleFields / areaTree,
并恢复上一轮重写时误删的 profession.myPoints / profession.pointsRank
- ToolDef:新增 extraFields* 声明(字典工具 + 参数名 + 「查询参数 → 表单字段」占位符映射 + 前置锚点)
- FormRequest:新增 ExtraFields(含 dependsOn / param / before / areaCall)
- AgentOrchestrator:抽出共用的 buildReadCall,据此下发 extraFields(栏目字段定义与区划树)
- ModuleFormParityTest:新增 4 条契约测试(字段顺序 / 提交方式 / 控件形态 / 动态字段声明)
|
2026-10-13 18:11:10 +08:00 |
|
lijing
|
cb8764082d
|
fix(form): 附件上传地址改用模块真实路由前缀,修请假佐证材料 404
build / build-push (push) Successful in 53s
用户上传请假佐证材料时请求打到 /homeleave/api/base/FileUpload,被网关直接 404。
根因:默认上传地址按 "/" + ToolDef.module + "/api/base/FileUpload" 拼 ——
把「模块 key」当成了路由前缀。而请假的 key 是 homeleave、真实前缀是
/home-leave(差一个连字符)。实测 /home-leave/... 回 302 走 CAS,
/homeleave/... 回 caddy 404。
- 新增 AgentOrchestrator.pathPrefix(module, baseUrl):从 agent.modules 的配置
地址推出站点根下的路径前缀;未配置时退回 key,尾部斜杠会被吃掉
(否则会拼出 //api/base/FileUpload)
- buildForm 的上传地址改用它拼接(作业仍走 uploadPathOverride,不受影响)
- 其余 4 个模块的 key 与前缀同名,行为不变(curl 实测 /campusnotice、
/dormnotice、/familyactivity、/coursehomework/api/file/Upload 均 302 可达)
- 补 2 条契约测试锁住这个行为;并修正 ModuleFormParityTest 里被一并写错的
homeleave 地址 —— 正是它把同一个错误复制进了测试,导致一直测不出来
|
2026-10-13 16:36:15 +08:00 |
|
lijing
|
53b3eaa39c
|
feat(form): 表单支持单选/勾选/快捷时段/逐天微调,请假登记表面对齐模块页
build / build-push (push) Successful in 1m28s
- ToolParam / FormField 新增控件能力:
radio(单选按钮组)、switch(勾选框+开关值映射)、quickPeriods(快捷时段)、
dailyExpand(按天展开)、autoPickFirst(默认选中第一项)、
hideIfSingleOption(单选项隐藏)、placeholder(照搬模块页原文案)
- LeaveTools:
* leave.submit / leave.register 的 leaveType 改单选按钮组并默认选中第一个
* 请假时间补齐快捷时段与「连续请假」勾选框(勾上展开连续天数 + 逐天时段)
* 事由 / 附件对齐模块页文案;学生端 classId 单班隐藏、教师端恒显示
- ToolExecutor.expandDailyArgs 认前端显式发来的 continuousLeaveTime
(用户在逐天时段里做过微调就以他改的为准,不再按基准覆盖)
- 只读查询因下游 302 失败时也回传 landingPath,让「去业务系统查看」按钮有着落
|
2026-10-13 16:15:51 +08:00 |
|
lijing
|
c16356db6e
|
feat(form): 发布表单与各模块「新建发布」页逐字段对齐(含审批流与作业两步发布)
build / build-push (push) Successful in 1m2s
把 5 个模块「新建/发布」页的字段与提交体逐个核对后,补齐此前缺失或形态不符的部分:
- 部门名镜像:公告/宿舍/活动三处在模块页是「字典 id + 反查名称」一起提交。
新增 mirrorLabelOf 机制 —— 该字段不渲染控件、不进模型 schema,提交时按源字段
选中项的显示名填充(deptName)
- 公告审批流:新增 flow 控件(widget=flow)+ 教师组织树字典 notice.teaTree,
提交 flow:[{name,vote,approverTeaIds}];publishType 值域修正为 direct/approve;
存草稿改用模块自己的独立接口 notice.saveDraft(POST /api/notice/SaveDraft)
- 活动分类:category 提交的是**分类名称**(模块页 :value="String(c.v)"),
optionsKeys 由 ("k","v") 改为 ("v","v")
- 请假附件:fileIds 在 HomeLeave 是 number[](JSON 数组),去掉逗号串拼接;
连续请假按 continuousLeaveNum 逐天展开成 continuousLeaveTime[](服务端/浏览器同源)
- 作业发布(整块新增):homework.publish / schedule / published / drafts /
teachingSubjects / classTree。模块没有一步发布的接口,用 ToolDef.then 声明两步提交
(Create 落草稿 → Publish | Schedule);附件是对象数组
attachments:[{fileId,fileUrl,fileType,fileName,sort}],上传走 file 域
/coursehomework/api/file/Upload
- 执行器:新增 followUp 两步提交(浏览器侧同源)、数组参数给标量时包成单元素数组、
首步响应 data 取资源 id(对象形态取 .id,避免拼出 /homeworks/{"id":1}/publish)
- 新增 ModuleFormParityTest(13 项)逐条锁住上述契约
回归:mvn clean package → Tests run: 103, Failures: 0, Errors: 0, BUILD SUCCESS
真机:headless Chrome 走「表单 → 浏览器代执行 → 模块接口」,50 项断言全绿
|
2026-10-13 11:29:03 +08:00 |
|
lijing
|
322d589b3e
|
feat(form): 发布表单字段与各模块「新建发布」页完全对齐
build / build-push (push) Successful in 54s
原来助手表单只渲染"缺失的必填项",且控件只有文本/单选/时间 —— 与各模块
自己的发布页相比缺了接收对象多选、附件、定时发送、置顶、紧急程度、回执单据、
发布方式等字段,用户没法"填得一样"。
补齐一层共享的表单能力,再把三个发布工具按模块页逐字段重写:
共享能力
- ToolParam: 新增 join(分隔符)/multiple/allOption(哨兵)/scopes(推送范围),
array 类型有选项来源时自动用多选控件;数组 schema 补 items
- ToolDef: 新增 fullForm() —— 表单渲染全部字段(不再只渲染缺失必填)
- ToolExecutor: POST_JSON 前按 joinSeparator 把数组拼成模块要的逗号串;
allToken 与具体项互斥(只选具体项→拼串,否则发哨兵)
- AgentOrchestrator: file 控件补 uploadPath(/<模块>/api/base/FileUpload)
- 模板/参数文件同步下发 joinSeparator/uploadPath/multiple/allToken/scopeType
三个发布工具
- notice.publish: 补重要程度/接收班级(pushScopes 对象数组)/置顶/短信/阅读回执/
附件/定时发送/发布方式;新增 notice.stuTree(组织树)
- dorm.publish: 补紧急程度/生效时间/回执截止/回执单据/接收宿舍(多选,ALL)/
宿管配合/置顶/短信/附件/定时发送/存草稿
- activity.publish: 补报名截止/回执方式三档/名额上限/家长随行/活动费用/
接收班级(多选,ALL)/短信/附件/定时发送/存草稿
- leave.submit/register: 连续请假改为可填参数,补连续天数与佐证材料附件
测试:发布工具契约 + 表单字段与模块页一致性断言;90 tests 全绿
|
2026-10-12 22:05:09 +08:00 |
|
lijing
|
b6a8a63b0e
|
feat(dorm,activity): 补齐宿舍通知与家校活动的发布/回执/台账能力 + 两个裸意图直达表单
build / build-push (push) Successful in 51s
两个模块与「校园公告」同构(发布 → 阅读情况 → 回执闭环),原先只接了「读列表 + 我提交」
一层,价值最高的 发布/盯回执/催办/统计 全空。本轮补齐。
宿舍通知(9 → 35 个工具)
- 发布闭环:dorm.publish(立即发布、默认发给全部宿舍)/ dorm.editNotice /
dorm.sendList(改指 /api/notice/SendList,我发布的)/ dorm.readDetail / dorm.statistics;
字典:dorm.noticeTypeDic(type=noticeType)/ dorm.deptDic(type=dept)/ dorm.formTemplates。
- 回执闭环:dorm.receipts / dorm.receiptDetail / dorm.reviewReceipt / dorm.remind /
dorm.expedite / dorm.remindStudent / dorm.markRead / dorm.pull / dorm.top;
家长侧补 dorm.initializeRed(一键已读)。
- 宿舍特有的床位台账(模块自带、此前完全没接):dorm.pendingStudents → dorm.assign
→ dorm.removeStudent / dorm.transfer / dorm.setMonitor,以及楼栋房间增改
(dorm.addBuilding / dorm.editBuilding / dorm.addRoom / dorm.editRoom)。
家校活动(7 → 22 个工具)
- 发布闭环:activity.publish / activity.edit / activity.sent / activity.readDetail /
activity.statistics;字典:activity.deptDic(type=1)/ activity.categoryDic(type=2)。
- 报名闭环:activity.signups / activity.signupDetail / activity.review /
activity.remind / activity.expedite / activity.pull / activity.top;
家长侧补 activity.initializeRed。
裸意图直达(IntentShortcut)
- 新增 DORM_PUBLISH(仅教职工)与 ACTIVITY_PUBLISH(仅教职工),学生落回模型如实说明。
- ★ 修跨模块歧义:「发通知 / 发布通知」不带限定时校园公告与宿舍通知都成立,
已从公告短语集移除,改由模型按上下文选;只认带「公告/校园」或「宿舍」「活动」限定的说法。
角色(RolePolicy)
- 发布/编辑/阅读情况/回执处理/催办/统计/字典/床位台账 → STAFF_ONLY;
家长侧(通知箱、回执提交、活动广场、报名、一键已读)保持不限。
表单下拉复用请假的 optionCall + valueKeys + dependsOn 通道(模块 /api/* 只认浏览器会话,
服务端必然 302)。发布表单的接收对象固定为「全部」(receiveType/receiveList 用 fix 写死),
避免表单里出现难用的房间/班级 id 输入框。
测试:新增 DormActivityPublishIntentTest(10);IntentDirectFormTest +4(教师得表单且不过大模型、
学生落回模型);NoticeIntentFormTest 增加「发通知不再被公告劫持」;
RolePermissionTest 补新工具的可见性断言。全量 86/86 通过。
README:4.2 能力划分更新 + 新增「宿舍通知/家校活动补全了哪些能力」;5.1.0 意图对照表补三行。
|
2026-10-12 19:49:52 +08:00 |
|
lijing
|
d2c6c185b5
|
feat(notice): 「发布校园公告」裸意图直达表单 + 修发文部门下拉与假的发布方式
build / build-push (push) Successful in 1m9s
- IntentShortcut 泛化成规则表(Rule{短语集 → Intent}):加新意图只需
① 一条 Intent ② 一个短语集 ③ RULES.add(...),不用动 AgentOrchestrator。
新增 NOTICE_PUBLISH —— 仅教职工(notice.publish),学生无落点则落回模型如实说明。
短语表只收带「发/发布」动词的说法(发布公告/发个公告/发布校园公告/发通知…),
光说「公告」「通知」更像查询,不收,避免误判。
- ★ 修 prepareOptionCall 漏拼字典工具的 fixed 参数:公文部门字典是
/api/dict/List?type=1,丢了 type 前端取回的是「没指定类型」的结果 → 空下拉。
- deptId 补 optionsKeys("k","v"):平台通用字典返回 {k,v} 而非 id/name;
服务端兜底 fetchDictOptions 的候选键也补上 k/v。
- ★ 移除 notice.publish 的 publishType 与 deptName:后端 /api/notice/Create 就是
直接发布(无 publishType 字段,是否转审批由学校「应用设置」绑定的流程决定),
声明它会让用户以为选了「存草稿」却仍然直接发出;deptName 由后端按 deptId 反查字典。
- system prompt 加第 10 条说明。
- 测试:新增 NoticeIntentFormTest(7),IntentDirectFormTest 加教师/学生两条公告用例
(仍用「一调就炸」的假 LLM 证明不过大模型);全量 71/71。
|
2026-10-12 18:54:15 +08:00 |
|
lijing
|
c11d331371
|
feat(leave): 裸意图直达表单 + 按身份分离请假表单 + 下拉代取通道
build / build-push (push) Successful in 51s
- 新增 IntentShortcut:说「我要请假」这类只有意图的短句时,
编排器在调模型前先做窄匹配(去空白标点 + 剥前后缀后正好等于标准说法、
且原句 ≤12 字),命中即直接下发表单并跳过 LLM,消除「偶尔不准」。
- 请假拆两个工具:leave.submit(type=2 学生申请,待审核)与
leave.register(type=1 教师帮助登记,免审,必填 classId+stuId)。
RolePolicy 分别归入 STUDENT_ONLY / STAFF_ONLY,模型视野里只有一个。
- ToolDef.fixed 由 Map<String,String> 改为 Map<String,Object>:
type/continuousLeave 必须按数字下发,字符串会被模块侧 Jackson 判非法。
- ToolParam/FormField 新增 optionsKeys(valueKey,labelKey) 与 dependsOn:
下拉改为下发 BrowserCall optionCall(含 {{占位符}}),由前端带用户登录态
代取后按 valueKey/labelKey 映射;stuId 依赖 classId,选完班级才解锁。
ToolExecutor#prepareOptionCall 不做必填校验,只拼占位查询串。
- 修 leave.types/myClasses/classStudents 描述,明确「填表时不要先调它」。
- README:4.2 能力划分更新 + 新增「请假为何分两个工具」;5.1 增加
5.1.0 裸意图直达(含身份对照表)与表单 JSON 示例。
- 测试:新增 IntentDirectFormTest(5)、LeaveIntentFormTest(9),
以「被调用即失败」的假 LLM 断言「我要请假」不经过大模型;
RolePermissionTest 补 leave.submit/register 的互斥断言。全量 62/62 通过。
|
2026-10-12 17:49:55 +08:00 |
|
lijing
|
61a3000bee
|
fix(auth): 当前身份优先按 roleName 判定,免疫角色号配置漂移
build / build-push (push) Successful in 51s
- matchCurrent 改为「先 roleName、再 roleId」:平台下发的 roleName(老师/校领导/学生/
家长/局领导/工作号/管理员)是平台权威标签,先认名称可避免部署环境里 agent.roles.*
被旧角色号覆盖时把身份判错(现象:老师账号显示成「学生」)
- ADMIN_WORDS 去掉「主任」(与「班主任」冲突会误判为管理员),补「教导」「职工」等
- 构造时 INFO 打印本次生效的角色配置,配置漂移一眼可见
- 测试 48/48 通过
|
2026-10-12 16:41:09 +08:00 |
|
lijing
|
67e55f1875
|
feat(auth): 角色映射按平台字典(1-7)校准,并移除资源库模块对接
build / build-push (push) Successful in 52s
- 角色字典实测校准(采样 200+ 账号 currentRole/roles):
1 老师 / 2 校领导 / 3 学生 / 4 家长 / 5 局领导 / 6 工作号 / 7 管理员
→ agent.roles: student=[3] teacher=[1,6] parent=[4] admin=[2,5,7]
(旧默认 student=[1]/teacher=[2,3]/admin=[4..7] 系臆测,导致老师账号被显示为学生)
- 新增家长身份 AgentRole.PARENT;活动报名/取消改由 STUDENT_OR_PARENT 放行
- 当前身份识别增加 roleName 关键词兜底,避免识别不出就降级为访客
- 移除资源库对接:删除 ResourceTools 及注册/编排/agent.modules/module-landing
- 测试 48/48 通过
|
2026-10-12 16:10:39 +08:00 |
|
lijing
|
57766c8bba
|
chore(ci): 空提交触发重新构建,为部署产生新镜像 tag
build / build-push (push) Successful in 51s
上一版镜像 tag 15b3c9da 已推送到 Nexus 且 docker-hosted 不允许覆盖已存在的 tag,
重跑同一提交会在「构建并推送镜像」步骤失败(blob upload invalid -
Repository does not allow updating assets)。
本提交无任何代码改动,仅用于产生一个新 tag(新 tag = 本次提交 sha 前 8 位),
让后端 CI 重新构建并入库镜像,并再次通知 projman 建档以供「后端发布」部署。
|
2026-10-12 14:10:55 +08:00 |
|
lijing
|
15b3c9da30
|
fix(llm): 修复 thinking mode 的 reasoning_content 协议 400,默认关闭深度思考
build / build-push (push) Successful in 1m31s
线上现象:用户发消息后助手回
「大模型错误:The `reasoning_content` in the thinking mode must be passed back to the API.」
根因:DeepSeek V4 系列(含 deepseek-flash)默认开启 thinking mode。官方协议规定,
只要请求带 tools 参数,历史里所有 assistant 消息的 reasoning_content 都必须原样回传,
否则接口直接回 HTTP 400。而 ChatMessage.toJson() 只输出 role/content/tool_calls/tool_call_id,
把该字段整个丢掉 —— 于是「第一轮模型调工具成功、第二轮请求必炸」。
改动:
- ChatMessage 增加 reasoningContent 字段并在 toJson 里输出(非空才输出);ChatResult 同步解析响应字段。
- AgentOrchestrator 新增 assistantOf(),把模型每次返回的思维链写回对应的 assistant 消息
(带 tool_calls 的那条与最终回复那条都要带)。
- DeepSeekClient:
* 新增 agent.llm.thinking(auto/enabled/disabled),按配置显式发送 thinking 字段;
* 命中 reasoning_content 协议错误时自动关闭 thinking 重试一次(自愈,线上无需等改配置即可恢复);
* humanizeApiError 增加该错误的中文话术,避免原样抛出英文协议报错。
- AgentSettings.Llm.thinking 由 boolean 改为三态字符串,默认 disabled:
本工程属工具密集编排,几乎每轮都带 tools,CoT 收益有限却显著增加延迟与 token,
且 thinking 模式下 temperature 会被忽略。
- application.yml 补 agent.llm.thinking: disabled 与说明。
新增 ThinkingModeTest(7 例):字段序列化/省略、disabled 与 auto 的请求体、响应解析、
命中协议错误时的自愈重试、以及非协议错误不被误重试。
|
2026-10-12 12:13:41 +08:00 |
|
lijing
|
0197a9de05
|
fix(llm): 大模型 api-key 启动自检,缺配/占位符直接 ERROR
build / build-push (push) Successful in 52s
线上容器漏注入 DEEPSEEK_API_KEY 时,api-key 会落到 application.yml 里的中文
占位符,直到某个用户发起一次对话才以 HTTP 401 暴露:
ResourceAccessException: I/O error on POST ...
java.net.HttpRetryException: cannot retry due to server authentication, in streaming mode
排查成本很高。
DeepSeekClient 增加 @PostConstruct 自检(可 grep [大模型]):
- 为空 / 仍含"请替换"占位符 -> ERROR,并写明修法
- 不以 sk- 开头 -> WARN 提示格式可疑
- 正常 -> INFO 打长度 + host + model(不打印 key 本体)
修法提示:在 projman 的 /app/config/application-prod.yml 填真实 key,
或给容器注入环境变量 DEEPSEEK_API_KEY(勿写入 public 仓库中的 application.yml)。
|
2026-10-12 11:57:49 +08:00 |
|
lijing
|
8303b857e9
|
feat: 提交校园智能助手后端工程源码(Spring Boot 2.7.18 + 平台 starter + JPA + Nacos)
build / build-push (push) Successful in 40s
- com.yunhai.agent,端口 8093,context-path /agent,制品 target/agent.jar;与 NoticeService 同构
- CAS 单点登录 + 三层角色权限(学生/教师/管理员/访客)+ 会话与工具调用审计落库
- DeepSeek 大模型编排 + 7 个业务模块工具(请假/公告/宿舍/活动/作业/资源库/教师发展)
- 浏览器代执行协议(服务端挂起 → 前端用登录态代调 → toolResult 续跑)+ 调用失败深链兜底
- 登出修复:服务端真正 session.invalidate() + 前端两步登出 + logout-guard
- 保留 projman 注入的 .gitea/workflows/build.yml 与 docker/Dockerfile;docker/application-prod.yml、run-app.sh 随工程入库
|
2026-10-12 11:18:29 +08:00 |
|
admin
|
8589afd69a
|
ci: update build workflow [skip ci]
|
2026-10-12 11:08:36 +08:00 |
|
admin
|
5850f46e7e
|
ci: init Dockerfile [skip ci]
|
2026-10-12 11:02:28 +08:00 |
|
admin
|
dd850d65b8
|
ci: init build workflow [skip ci]
|
2026-10-12 11:02:27 +08:00 |
|
admin
|
afd3660e96
|
Initial commit
|
2026-10-12 11:02:24 +08:00 |
|