becomer
|
c1573f77ac
|
v1.2.97:邀请逻辑整改(登录去邀请码 + 推广信息合并 + 填邀请码绑定)
ci/woodpecker/tag/build Pipeline was successful
1. 登录不再需要邀请码:登录页移除邀请码展示/URL ?invite= 传参;登录/注册不再携带
邀请关系;取消「直接注册奖励(register_reward)」。新用户注册不发放任何积分。
2. 底部导航「个人中心」→「我的」。
3. 个人中心将「邀请链接」「邀请信息」合并为「推广信息」卡片:
- 左:邀请码 + 复制按钮(改为只复制邀请码)
- 右:奖励积分 + 兑换按钮
- 下方:邀请好友即可获得 x 积分
- 无邀请人用户显示「填写邀请码」→ 弹窗输入 → 提交校验设备码(未绑定提示
「请先绑定设备码」)→ 绑定邀请人并给双方发积分(每人仅可绑定一次)。
4. 后端新增 POST /auth/bind-invite;GetMyInviteInfo/Generate 返回 hasInviter。
5. 新手引导文案与锚点更新(去掉登录携码/链接语义),GUIDE_VERSION v2→v3。
6. 后台设置移除「直接注册奖励积分」项。
v1.2.97
|
2026-09-29 11:40:31 +08:00 |
|
becomer
|
d588d10c4c
|
v1.2.96:底部导航移除网课 + 「可执行次数」文案 + 置顶公告满宽右→左无缝滚动
ci/woodpecker/tag/build Pipeline was successful
1. 底部导航栏取消「网课」tab。
2. 个人中心「可签到次数」改为「可执行次数」(VIP 文案同步为「VIP 执行次数」)。
3. 置顶公告:去掉 warning 边框、整行满宽;改为右→左循环滚动,渲染两组内容
translateX(-50%) 无缝接回开头,单条也持续滚动。
v1.2.96
|
2026-09-29 10:39:38 +08:00 |
|
becomer
|
7dac485769
|
v1.2.95:兑换弹窗验证码错误改为在验证码行提示,并在失败后自动刷新验证码
ci/woodpecker/tag/build Pipeline was successful
- 问题:CDK 兑换/VIP 续费弹窗中,验证码错误或过期时错误红字显示在「兑换码」框下方,
让用户误以为是兑换码出错。
- 修复:验证码类错误(message 含「验证码」)改为在「验证码」输入行下方红字提示,
兑换码框不再误标红;验证码错误后自动刷新验证码并清空输入,便于直接重试。
v1.2.95
|
2026-09-29 10:22:26 +08:00 |
|
becomer
|
195702c14f
|
v1.2.94:修复销假图片上传入口被 enableAttach 隐藏导致无法上传附件
ci/woodpecker/tag/build Pipeline was successful
- 现象:用户 25074105001(吉林医药学院,旧套)销假时表单只显示经纬度/地址,
提交报「该请假销假需要上传图片附件」,无法完成销假。
- 根因:旧套 leaveType.enableAttach 字段不可靠(多所旧套学校恒为 0),
前端以此字段控制销假图片上传入口显隐,导致该字段为 0 时上传入口不显示;
而部分学校销假实际强制要求附件(提交返回 2210990000)。
- 修复:销假图片上传入口改为始终显示(可选,不填则不携带),
并附提示「部分学校销假要求上传,如提交提示需要附件请在此上传」。
v1.2.94
|
2026-09-28 15:26:42 +08:00 |
|
becomer
|
df550bf2ac
|
v1.2.93:工单未读回复徽标 + 公告面板未关闭工单通知排序靠前
ci/woodpecker/tag/build Pipeline was successful
1. 个人中心「工单与反馈」入口新增未读回复条数徽标(工单通知未读数)。
2. 右上角公告(铃铛)面板:工单通知分区把「未关闭工单」的通知排到前面
(后端 ListTicketNotifications 附带 ticketStatus,前端按 status≠closed 优先排序)。
v1.2.93
|
2026-09-28 15:21:00 +08:00 |
|
becomer
|
75a642f3a2
|
v1.2.92:兑换签到次数弹窗增加「邀请积分可免费兑换」引导提示
ci/woodpecker/tag/build Pipeline was successful
当邀请奖励积分满 1(canRedeem,可免费兑换)时,在「兑换签到次数」弹窗内
提示用户关闭本弹窗,改用下方「邀请信息」处的兑换按钮用积分兑换(无需兑换码)。
v1.2.92
|
2026-09-28 11:32:07 +08:00 |
|
becomer
|
d2ca858816
|
v1.2.91:修复请假审批人获取失败被静默吞掉导致误报「未配置审批人」
ci/woodpecker/tag/build Pipeline was successful
- 现象:用户 124509440(西藏民族大学)提交请假时提示「未配置审批人」,
实际学校有 2 个审批人(谷一墨、黄操)。经 headless 浏览器复现,根因是:
当请假起止时间填写有误(如开始≥结束)时,后端 getApproveFlowList 返回
「结束时间要晚于开始时间」,前端 loadApplyApprovers 的 catch 静默清空候选,
界面遂显示「该校该请假类型未配置审批人,可不选」,误导用户以为接口取不到人。
- 修复:捕获并展示真实错误(applyApproversError),该错误时不再显示
「未配置审批人」文案;切换类型/时间时重置错误态。
- 说明:后端各接口实测正常(对多种类型/时间均返回 2 个审批人),本次仅改前端提示。
v1.2.91
|
2026-09-28 11:23:15 +08:00 |
|
becomer
|
84f746fa2d
|
v1.2.90:后台用户管理执行次数弹窗补全「返校登记」类型中文
ci/woodpecker/tag/build Pipeline was successful
taskTypeLabel 未覆盖 return_register,导致该类型日志显示英文原名,
补上「返校登记」中文映射。
v1.2.90
|
2026-09-27 18:25:20 +08:00 |
|
becomer
|
7f81d54ea2
|
v1.2.89:返校登记接入打卡任务体系(按 taskType 分流)
ci/woodpecker/tag/build Pipeline was successful
- 新增任务类型 return_register,与签到/查寝共用统一 task 接口
(titles/create/list/form/update/delete/execute/logs),按 taskType 走不同逻辑。
- 后端:
- TaskType 增加 return_register;FetchAllRecentTasks 聚合返校登记待办为任务标题
(SignWid="taskWid:stuWid")。
- ListTasks 对 return_register 按待办集合判定 matched_unsigned/matched_signed。
- GetTaskForm 无形表单(返回空);ExecuteTask 分流到 executeReturnRegisterTask
(取配置校区坐标提交确认到达,扣打卡次数、VIP 免扣、写日志,SSE 体验一致)。
- 移除 v1.2.88 的 /leave/return-register/* 路由与 handler。
- 前端:打卡页(HomeView)接入——标题列表出现返校登记、选中即创建(跳过名字匹配校验、
无形表单直接保存)、列表/日志显示"返校登记"标签(琥珀色)、执行复用同一 SSE。
移除请销假页的返校登记卡片与 API 封装。
v1.2.89
|
2026-09-27 18:06:31 +08:00 |
|
becomer
|
a802912a55
|
v1.2.88:新增节假日离返校「返校登记」(确认到达)
ci/woodpecker/tag/build Pipeline was successful
- 背景:部分学校(如合肥大学)在「节假日离返校」应用要求假期结束做返校登记,
原系统只支持 信息收集/签到/查寝 等,无法处理该类型。
- 通过应用大厅 getAppInfos 枚举定位到 wec-counselor-leave-return-apps 应用;
实测该校接口:queryReturnConfigList(校区坐标+半径)、queryStuRegisterList(列表)、
submitStuReturnRegisterInfo(提交返校位置)。
- 待办判定:status=="1" 且 returnRegisterInfo 为空;提交参数
{taskWid,stuWid,returnAddress,returnLongitude,returnLatitude,returnSignType}。
- 后端:leave_return.go 服务 + handler + 路由 /leave/return-register/{pending,execute}
(默认用学校配置校区坐标,范围内登记;支持范围外带原因)。
- 前端:请销假页展示「返校登记」卡片,有则显示、一键提交。
v1.2.88
|
2026-09-27 17:31:24 +08:00 |
|
becomer
|
9074118889
|
v1.2.87:修复西藏民族大学登录(定制 CAS,教科书 RSA 小端 hex 密码)
ci/woodpecker/tag/build Pipeline was successful
- 该校平台标记 joinType=CLOUD(IsIap=true),但网关 IAP 账密接口不可用,
提交报 FAIL_UPNOTMATCH("用户名或密码不匹配"),误导为密码错误。
- 实测真实链路:/iap 302 → cas.xzmu.edu.cn/lyuapServer/login(定制 CAS)→
表单 POST /lyuapServer/v1/tickets(密码加密方式特殊)→ 得 TGT/ST →
回调 /iap/loginSuccess?sessionToken=...&ticket=ST 建立网关会话(MOD_AUTH_CAS)。
- 关键差异:密码加密为该站前端库的"教科书式 RSA(无填充)+ 小端序零填充 + hex",
与通用 wisedu 的 PKCS#1 v1.5 完全不同(用 PKCS#1 会被解成乱码 → PASSERROR)。
- 新增 login_xzmy.go 单独适配该校(真实账号 124509440 实测登录成功),
并在 CpdailyLogin 分派;含加密/票据解析/错误码翻译单测。
v1.2.87
|
2026-09-27 14:12:24 +08:00 |
|
becomer
|
176c43251c
|
v1.2.86:新套请假支持手写承诺书签名(isSign 类型)
ci/woodpecker/tag/build Pipeline was successful
- 背景:部分新套学校(如成都职业技术学院)请假要求先手写《请假安全承诺书》
并上传签名图,缺失时服务端报「手写承诺书不能为空」。
- 字段来源:抓取官方 H5(prod v0.0.279)SafetyLetter/Apply chunk 反查确认——
签名图经 OBS getUploadPolicy 上传后,作为 attachs 中
{attachType:1,attachUrl,attachSize,attachName:"学生手写承诺书",obsKey,isStuSign:true} 提交。
- 后端:类型列表透传 requireSign(queryLeaveType.isSign);提交表单支持
signatureUrl 生成签名 attach;提交前复核未签名直接拦截。
- 前端:选中 requireSign 类型即弹出 canvas 手写签名板,签名图上传 OBS 后随提交携带。
v1.2.86
|
2026-09-26 18:04:22 +08:00 |
|
becomer
|
839637b3d3
|
v1.2.85:置顶公告改为「标题:详情」循环滚动,且不进入用户公告列表
ci/woodpecker/tag/build Pipeline was successful
- 打卡页顶部置顶公告由「仅标题,点击弹详情」改为「标题:详情」整体左右循环滚动播放。
- 置顶公告从用户公告列表、未读数与聚合信息中排除,也不可被标记已读。
- 置顶仅对普通用户公告开放(后台开关在选代理时禁用并复位,后端同步校验)。
v1.2.85
|
2026-09-25 13:31:35 +08:00 |
|
becomer
|
5ee8e47b0f
|
v1.2.84:扣款额度可配置(支持小数/0)+ 置顶公告横向滚动
ci/woodpecker/tag/build Pipeline was successful
- 新增系统设置 deduct_checkin/deduct_leave/deduct_report(默认 1),
打卡/请假/销假从数据库读取扣减额度,支持小数与 0(0=不扣费);
sign_count 由 int 改为 decimal(12,2) 以支持小数次数。
- 新增置顶公告:announcements.is_pinned 字段 + /announcements/pinned 接口,
打卡页顶部横向无缝滚动展示,点击查看详情;后台公告管理支持置顶开关与标识。
- 后台系统设置新增三项扣款额度配置;用户/后台的签到次数展示改为去尾零格式。
v1.2.84
|
2026-09-25 13:07:14 +08:00 |
|
becomer
|
af24472e2a
|
v1.2.83:新建打卡任务名称增加匹配校验——改到匹配不上现有任务时直接提示并拦截
ci/woodpecker/tag/build Pipeline was successful
新建流程 Step 1 编辑任务名称后此前不校验,而执行打卡时后端按该名称 globMatch
实时匹配今日校园任务实例(FetchTodayTaskDetail),名称改坏即永远匹配失败。
现前端按与后端一致的 globMatch 规则、限同 taskType 比对现有任务名,命中则实时
提示,未命中在点「下一步」时弹窗拦截,不创建任务也不进入 Step 2。
v1.2.83
|
2026-09-23 23:30:12 +08:00 |
|
becomer
|
518f3506df
|
v1.2.82:修复新套请假审批人查询失败——时间格式统一裁秒
ci/woodpecker/tag/build Pipeline was successful
新套 getApproveFlowList 按 "yyyy-MM-dd HH:mm" 严格解析(如 hbgyzy 病假类型 7151
报 unparsed text found at index 16),前端补秒的 19 字符时间导致接口报错,
前端静默降级后误显示"该校该请假类型未配置审批人"。在 FetchLeaveNewApproveFlow
服务层统一裁秒,覆盖前端预取与提交执行两条调用路径。
v1.2.82
|
2026-09-23 00:14:53 +08:00 |
|
becomer
|
21b37c6ad8
|
v1.2.81:请销假链路健壮性与设备码校验
ci/woodpecker/tag/build Pipeline was successful
- leavePost 统一校验 HTTP 状态码与空 body:非 2xx 报「学校服务端响应异常(HTTP xxx)」,
空 body 报「可能触发风控或瞬时故障」,不再抛出无意义的 JSON 解析错误
- 旧套销假识别 2210990000(需附件):提示上传附件后重试
- 打卡/请假/销假执行入口补后端设备码硬校验;请假页前端入口补绑定引导弹窗
- SMS 注册密码不再使用设备码样式的随机值(避免与设备码绑定语义混淆)
v1.2.81
|
2026-09-22 19:14:26 +08:00 |
|
becomer
|
a7180eebba
|
v1.2.80:根治 cookie 失效误报——统一识别登录态失效并自动续期
ci/woodpecker/tag/build Pipeline was successful
① 问题:用户赵佳威(芜湖职业技术大学)反馈「无法获取签到任务」。
日志显示 whit.campusphere.net 两类查询均报
"解析任务列表响应失败: invalid character '<' looking for beginning of value"
—— 今日校园返回 HTML(请求被 302 到 authserver 登录页),即 cookie 已失效。
其 MOD_AUTH_CAS 票为 09-11 签发、早已过期;而 last_login_at 显示 09-21 是
jwt 中间件 touchDailyLogin 刷新的"当天使用"标记,并非真实重登,故造成
"看起来刚登录过、却取不到任务"的假象。
② 根因:原 isCookieExpired 仅匹配错误文案「未登录/登录已过期」,而 HTML 响应的
报错是 invalid character '<',匹配不到 → 既无法识别、更不会触发续期,
用户只看到空任务列表。该问题影响所有「已登录后调用今日校园」的接口
(任务列表、用户信息同步、请销假等)。
③ 修复:
- cpdaily 层新增 ErrCookieExpired 与 unmarshalBody:解析响应体前先判断
是否为 HTML,是则返回 ErrCookieExpired(42 处 body 解析统一接入;
登录/发码/滑块等登录流程不涉及用户 cookie,保持原样不误伤);
- FetchAllRecentTasks 改为返回 error(两类查询都失败时),供上层识别续期;
- isCookieExpired 合并 ErrCookieExpired 判定 → 既有 25 处调用点自动受益;
- 新增 withCookieRefresh(通用包装):检测到登录态失效 → refreshCpdailyCookie
(长期凭证优先,其次密码重登)→ 用新 cookie 重试一次;续期失败返回
ErrCookieExpired,上层转「登录凭证已过期,请重新登录」;
- 应用于 GetTaskTitles、GetPendingLeaves(含新/旧套件)、syncUserProfile(用户信息)。
- 防递归:refreshCpdailyCookie 内部同步用户信息时禁续期
(syncUserProfileWith allowRefresh=false),避免「续期→同步→再续期」风暴。
④ MFA 学校说明:MFA 通道登录签发短期 JWT,到期即要求重新登录,本就不需自动续期;
非 MFA 学校(如本例)的 cookie 由上述链路自动续期,用户不再被莫名踢出。
⑤ 验证(真实数据,只读):
- 用赵佳威过期 cookie 调 FetchAllRecentTasks:由原来的 invalid character '<'
变为 cpdaily_cookie_expired,errors.Is(err, ErrCookieExpired)=true;
- 模拟完整链路:旧 cookie 失败 → 用其保存密码重登成功(6 个 cookie、含新 MOD_AUTH_CAS、
无 MFA)→ 新 cookie 重试成功取到任务「查寝」;
- go build / go vet / go test ./... 全过。
v1.2.80
|
2026-09-21 16:57:21 +08:00 |
|
becomer
|
386c6e2bf8
|
v1.2.79:工单改为软删除——用户删除后管理员仍可见(保留处理痕迹)
ci/woodpecker/tag/build Pipeline was successful
① 问题:用户可删除自己的工单,且管理员完全看不到。根因是 Ticket 无软删除,
删除为物理删除并级联删除全部消息,记录从表中消失,管理端列表/统计均无痕迹,
用户可借此抹掉工单内容与管理员的全部回复。
② 修复:
- model.Ticket 增加 DeletedAt gorm.DeletedAt(AutoMigrate 自动加列,
既有数据 deleted_at 为 NULL 视为未删除,不受影响);
- DeleteTicket 改为软删除,且不再级联删除 TicketMessage / TicketNotification,
保留完整对话与通知,供管理端追溯;
- 管理端 AdminListTickets / AdminGetTicket / AdminReplyTicket /
AdminUpdateTicketStatus 改用 Unscoped(),可查看并继续处理已删工单,
列表与详情响应新增 deleted 字段;
- 用户端(ListMyTickets / GetTicketDetail / AddTicketMessage / CloseTicket)
不加 Unscoped,GORM 默认过滤 deleted_at,用户看不到自己已删除的工单;
未关闭工单计数同样自动排除已删除(不再计入"进行中")。
③ 前端:管理员工单列表与详情在状态标签旁展示「用户已删除」标记
(含 title 提示);api/ticket.ts 的 AdminTicketListItem / TicketDetail 增加 deleted。
④ 验证:在真实 MySQL 上用临时表(tmp_softdel_probe,验证后已删除)确认三项语义——
默认查询过滤软删除(count=0)、Unscoped 可见且 deleted_at 可读、
管理端 Unscoped().Table().Select("t.*") + 内嵌结构体扫描能读出 deleted_at;
go build / go vet / go test ./... 通过;vue-tsc --noEmit、npm run build 通过。
注:此前已被物理删除的工单无法找回,本改动仅对之后生效。
v1.2.79
|
2026-09-21 13:35:39 +08:00 |
|
becomer
|
2dd1180bf8
|
v1.2.78:修复手机号登录失败无提示、页面被重置回登录页
ci/woodpecker/tag/build Pipeline was successful
① 现象(用户实测手机号 13551503012):点击登录后页面看似跳转,
实则被重置回 /login、输入信息清空,且没有任何错误提示。
② 根因:后端 SmsLogin 登录失败返回 HTTP 401(如「登录失败: 验证码错误…」),
而前端 axios 响应拦截器把 401 一律视为「登录态过期」→ 尝试用 refresh_token
刷新 → 登录页无 refresh_token → catch 清除 access/refresh token 并
window.location.href='/login'。
既有白名单只排除了 /auth/login 与 /auth/refresh,新增的 /auth/sms/* 未覆盖
('/auth/sms/login' 并不包含子串 '/auth/login'),故被误判。
真实错误消息(后端 message)也随之被吞掉,用户只看到页面被重置。
③ 修复(frondend/src/api/index.ts):401 刷新分支增加
!originalRequest.url?.includes('/auth/sms/') 排除项,
使手机号登录(发码/登录)的 401 作为普通业务错误抛给调用方 catch,
由登录页 toast 展示真实原因。
④ 实测复现与验证:容器内用真实图片验证码调用 /api/auth/sms/login,
服务端返回 401 {"code":401,"message":"登录失败: 验证码错误次数上限5次,您已输错1次"},
与该手机号毕业/验证码状态一致的业务错误;修复后该消息会正常显示而非重置页面。
vue-tsc --noEmit、npm run build 通过。
v1.2.78
|
2026-09-20 09:42:16 +08:00 |
|
becomer
|
a0064881b3
|
v1.2.77:手机号登录增加两个独立图片验证码(发码 / 登录各一)
ci/woodpecker/tag/build Pipeline was successful
① 需求:手机号登录的「获取验证码」与「输入验证码登录」两步都需要各自的图片验证码,
且两者相互独立(不共用同一个 captchaId)。
② 后端(backed/internal/handler/sms_login.go):
- SmsSendRequest / SmsLoginRequest 增加 captchaId + captchaCode 字段;
- SmsSend 与 SmsLogin 各自调用 service.VerifyCaptcha 校验,
缺失或错误分别返回「请输入图片验证码」/「验证码错误或已过期」。
(原 SmsSend 仅有 IP 限流,现补上图片验证码,防脚本刷短信。)
③ 前端(LoginView.vue + api/auth.ts):
- 新增两组独立验证码状态:发送用(smsSendCaptcha*)与登录用(smsLoginCaptcha*),
各自有 id / image / 输入 / 错误 / loading;
- 通用刷新函数 loadSmsCaptcha('send'|'login');进入手机号模式时两组各取一份;
- 手机号模式下渲染两个验证码输入框(发送前一个、登录前一个);
- sendSmsCode / smsLogin 增加 captchaId + captchaCode 参数;
- 发码失败或登录失败分别刷新对应验证码。
④ 邀请码兼容:手机号登录路径继续透传 inviteCode(URL ?invite= 自动带入),
新用户注册时经通用注册流程发放邀请/注册奖励;已有账号匹配时不重复发奖。
⑤ 验证:go build / go vet 通过;vue-tsc --noEmit、npm run build 通过。
v1.2.77
|
2026-09-19 23:41:43 +08:00 |
|
becomer
|
4ca952f2ac
|
v1.2.76:新增手机号验证码登录(无需选学校)+ 与学工号登录账号互通
ci/woodpecker/tag/build Pipeline was successful
① 手机号验证码登录(对标旧项目 likedream-go login/sms.go):
- 短信发码:POST https://mobile.campushoy.com/app/auth/authentication/mobile/messageCode/v-8222
(手机号 AES 加密后以 a=参数提交);
- 验证码登录:POST .../mobileLogin/v-8222,返回 a=base64(AES(json)),解密后含
tenantId(学校租户)、openId(学工号)、sessionToken、tgc(CASTGC);
—— tenantId 由登录响应直接给出,故**无需用户先选择学校**;
- 换取 CAS cookie:GET {学校域名}/wec-im-group/group/imUserToken/getImUserToken,
带 AmpCookies 头(DES 加密 {AMP1,AMP2:[{name:sessionToken}]})与预置
clientType/tenantId/standAlone/sessionToken/CASTGC cookie,换回含 MOD_AUTH_CAS 的 cookie。
② 长期凭证续期:cookie 含 sessionToken(长期有效),cookie 过期时优先用
RefreshCasCookie 直接换取新 CAS cookie,不再依赖 raw_password——
手机号验证码登录的账号没有密码,原「密码重登」路径无法续期;
续期失败再回退原密码重登逻辑(学工号登录用户不受影响)。
③ 账号互通(兼容手机号与学工号登录指向同一用户):
- 手机号登录:按「同校 + 同学工号(openId)」匹配已有账号并复用;未命中则按
用户名(学校短码+学工号)再匹配;仍未命中才走通用注册流程(复用邀请/注册奖励);
- 手机号登录后回填 student_id 与 mobile_phone,使其之后用学工号登录也能匹配到;
- 学工号登录(findOrCreateUser)增加「同校 + 同学工号」兜底匹配,
反向兼容「先手机号注册、后学工号登录」的场景。
④ 前端登录页:新增「学工号登录 / 手机号登录」切换;手机号模式下仅需
手机号 + 短信验证码(含 60s 倒计时),不选学校。
⑤ 验证:go build / go vet / go test ./... 通过;vue-tsc --noEmit、npm run build 通过。
加密键与旧项目一致(AES-128-CBC / DES-CBC,PKCS7),复用既有 crypto.go。
v1.2.76
|
2026-09-19 23:14:29 +08:00 |
|
becomer
|
edeabb0fc0
|
v1.2.75:回滚「每日签到积分」功能(保留验证码框修复)
ci/woodpecker/tag/build Pipeline was successful
v1.2.74 的签到积分功能经评估效果不佳,按需求回滚:
- 移除 User.checkin_points 字段;
- 移除 POST /api/auth/checkin 接口及路由;
- 打卡/请销假恢复为仅扣 sign_count(移除「优先消耗 1 积分」分支与 deductQuota);
- 移除个人中心「签到积分」入口、签到弹窗与 api/auth.ts 的 checkin 方法及类型字段。
保留 v1.2.74 中的「验证码框留白修复」(与签到积分无关):
- 个人中心验证码容器 w-24 → w-[120px](3:1 匹配生成图 180x60)、图片 object-cover;
- 登录页两处验证码图片 max-w-full/max-h-full object-contain → w-full h-full object-cover。
验证:grep 确认无 checkin 残留;go build / go vet 通过;vue-tsc --noEmit 通过。
v1.2.75
|
2026-09-19 11:41:42 +08:00 |
|
becomer
|
559b146408
|
v1.2.74:新增每日签到积分(个人中心)+ 打卡/请销假优先消耗积分;修复验证码框空隙
ci/woodpecker/tag/build Pipeline was successful
① 签到积分体系:
- User 新增 checkin_points(decimal(10,4),0~1),AutoMigrate 自动加列;
- 新增 POST /api/auth/checkin:校验已有图片验证码后,随机发放 0.01~0.30
积分(保留 4 位小数),封顶 1.0;达 1 后不可再签到(原子条件更新防并发超发);
- 个人中心新增「签到积分」入口:显示 当前积分/1 与签到按钮(达上限置灰「已达上限」),
弹窗内含积分规则、当前积分与图片验证码。
② 打卡 / 请销假改为优先消耗签到积分:
- 额度检查与扣减逻辑由「仅 signCount」改为「积分≥1 优先扣 1 积分,否则扣 1 次数」;
- 覆盖 3 处执行点:任务执行(task.go)、销假新套与旧套(leave.go),
抽公共函数 deductQuota 复用;均用条件更新原子扣减;VIP 仍免扣。
③ 修复验证码框留白:
- 生成图为 180x60(3:1,代码强制下限),而个人中心验证码容器原为 w-24 h-10(2.4:1),
图片按宽度缩放后上下留空。统一容器宽度为 w-[120px](3:1),
并把图片改为 w-full h-full object-cover 填满(登录页两处同改)。
④ 验证:go build / go vet 通过;vue-tsc --noEmit、npm run build 通过。
v1.2.74
|
2026-09-19 11:15:25 +08:00 |
|
becomer
|
d8f2c3676c
|
v1.2.73:待销假列表过滤已驳回的请假记录
ci/woodpecker/tag/build Pipeline was successful
① 问题:销假时「待销假列表」出现已驳回状态的请假记录(用户 陈莎莎/1038
即是此情况),这类记录无法销假,不应出现在列表中。
② 根因:旧套待销假列表 FetchLeaveList 只过滤了 status=="4"(已完成),
未过滤驳回状态。实测该用户旧套接口返回的记录 status="3"(前端映射「已驳回」,
事假/教资认定),被原样放进列表。
(新套 fetchLeaveNewPendingList 已按 3/4 白名单过滤,无此问题;该校走旧套。)
③ 修复:FetchLeaveList 中一并过滤驳回状态——
status=="3"(已驳回)与 status=="10"(逾期自动驳回)。
④ 验证:go build ./... 通过。只读实测确认该用户旧套记录 status=3 已被排除。
v1.2.73
|
2026-09-19 10:54:12 +08:00 |
|
becomer
|
cd963d3834
|
v1.2.72:修复用户管理执行弹窗「已删除」标签内部换行
ci/woodpecker/tag/build Pipeline was successful
v1.2.71 把状态列改为纵向排列后,标签仍缺 whitespace-nowrap,
在窄列内文字被折行(「已删/除」)。为「成功/失败」与「已删除」两个标签
补上 whitespace-nowrap,保证文字单行显示。
v1.2.72
|
2026-09-19 10:44:01 +08:00 |
|
becomer
|
c5e0888639
|
v1.2.71:修复后台 CDK 复制无效;用户管理执行弹窗状态标签换行
ci/woodpecker/tag/build Pipeline was successful
① 用户管理「当日执行」弹窗:状态列的「成功/失败」与「已删除」两个标签原在同一行
(td 带 whitespace-nowrap,列宽 w-[60px]),在窄列下被挤到溢出、视觉上串到
「说明」列。改为纵向排列(flex-col + gap-1,各占一行),表头宽度放宽到 w-[72px]。
② CDK 管理复制「提示成功但实际粘贴不出内容」:
根因是 copyToClipboard 把临时 textarea 挂到 document.body,而复制按钮位于
AppDialog(reka-ui)弹窗内——DialogContent 用 FocusScope 锁定焦点,document 上的
focusin 监听发现焦点跑到 dialog 容器外就立即抢回,导致 ta.focus()/select() 失效,
选区为空;execCommand('copy') 仍返回 true,于是「显示已复制、实际为空」。
修复:
- textarea 优先挂到当前打开的 dialog 容器内(role="dialog" 中最靠后可见者),
避免焦点被 FocusScope 抢回;
- 复制前校验选区确实落在 textarea 上(selectionStart/End 与 activeElement),
校验不过则不再盲信 execCommand 的返回值,转而走 Clipboard API 兜底;
- 移除 textarea 改为按实际父节点(不再假设是 body)。
该修复同时惠及所有位于弹窗内的复制按钮(CDK 单个/全部复制、待审学校等)。
注:AdminUsers 的设备码复制(copyDeviceCode)未在本次范围内,保持原样。
v1.2.71
|
2026-09-19 10:38:42 +08:00 |
|
becomer
|
3bcc32b74e
|
v1.2.70:网课页新增「我有网课,点击这里」贴边按钮(跳转工单)
ci/woodpecker/tag/build Pipeline was successful
模仿打卡页「收藏本站」与请假页「无法请/销假?提交工单」的右侧贴边窄竖条样式,
在网课页加入「我有网课,点击这里」按钮,点击跳转 /ticket 工单页。
- CourseView.vue:新增按钮(fixed right-0 bottom-32, bg-rose-500/95)与 goTicket();
- 复用同款竖排文字样式 .bookmark-vtext(writing-mode: vertical-rl)。
v1.2.70
|
2026-09-18 22:36:27 +08:00 |
|
becomer
|
8de7470b9d
|
v1.2.69:底部 TabBar 新增「网课」入口(开发中占位页)
ci/woodpecker/tag/build Pipeline was successful
① 需求:底部导航新增「网课」,放在个人中心左侧(第三个位置):
打卡 → 请/销假 → 网课 → 个人中心。
② 改动:
- 新增 src/views/course/CourseView.vue:顶部标题栏 + 居中占位「正在努力开发中」;
- src/layouts/MobileLayout.vue:tabs 数组插入 { name: 'course', label: '网课', icon: BookOpen },
并补充 BookOpen 的 import;
- src/router/index.ts:MobileLayout 子路由新增 /course(置于 leave 与 profile 之间),
注册 CourseView。
③ 说明:网课页不走 home/leave 的顶部蓝色渐变头(第 258 行仅这两者显示),
使用自身标题栏。引导流程仅涉及 home/profile,不受影响。
v1.2.69
|
2026-09-18 22:20:49 +08:00 |
|
becomer
|
c90784de67
|
v1.2.68:编辑表单优先取历史记录的真实经纬度/地址(并校验在打卡范围内)
ci/woodpecker/tag/build Pipeline was successful
① 问题:编辑打卡任务表单时,经纬度与地址取到的是学校配置的"打卡范围信息"
(如 signPlaceSelected[0].address = "新建的签到地点"),而非历史记录里的真实
签到位置。实测用户 1095(兰州财经大学,晚归寝打卡)确认。
② 根因:ParseTaskForm 的定位取值顺序是「signPlaceSelected[0] 优先」——
该字段是学校配置的范围点(含范围点名与范围点坐标),被排在历史记录的实际
签到经纬度之前,于是历史真实坐标/地址永远取不到。
③ 修复(backed/internal/service/cpdaily/task.go):
- 改为「历史记录优先」:先取 histRows 的 longitude/latitude 与 signAddress/position;
- 但新增范围校验 coordsWithinSignRange:历史坐标必须落在打卡范围内
(复用已有 parseSignPlaces + haversineMeters,逐点比对 radius);
超出全部范围点时放弃历史坐标与地址,回退到 signPlaceSelected[0] 范围点,
避免服务端以"位置不在签到范围内"拒绝提交;
- 无范围点配置时不做限制(沿用历史坐标);历史坐标缺失同样回退范围点。
- 无历史记录(histRows == rows)时行为与改动前一致,无回归。
④ 验证(真实数据,只读):
- 修复前 lon=103.89427283364513 / lat=36.053086828712225 / address=新建的签到地点;
修复后 lon=103.894854 / lat=36.052122 /
address=中国甘肃省兰州市城关区拱星墩街道段家滩路426号(历史真实值);
- 实测该历史坐标距范围点 119.3m < 240m,判定在范围内 → 正确采用历史值;
- 超范围/空坐标/无范围点三类边界均按预期;
- 无历史记录分支输出与改动前一致。
- go build / go vet / go test ./... 全过。
顺带修正 v1.2.66 调序后未同步的两个测试断言(二维码项已移至末尾)。
v1.2.68
|
2026-09-18 21:48:33 +08:00 |
|
becomer
|
186af6b604
|
v1.2.67:修复 MFA 登录报「生成验证会话失败」——运维配置 Redis 改用容器名
ci/woodpecker/tag/build Pipeline was successful
① 问题:中国矿业大学(cumt,需 MFA)登录报「生成验证会话失败」。
日志证据:MFA参数解析 userId=TS26040188P31(中国矿业大学)后,
ERROR MFA会话暂存失败 error="dial tcp 172.22.0.7:6379: connect: connection refused"。
② 根因:宿主机 /opt/cpdaily-xiaozhushou/config.toml 的 [redis] host 写死为
"172.22.0.7"。该 IP 曾是 Redis 容器地址,但容器重启后 IP 漂移,现已被
likedream-go 占用;原 Redis 现为 172.22.0.8 / likedream-redis 为 172.22.0.6。
即写死 IP 的经典陷阱:容器重建后 IP 易主,配置指向了非 Redis 的容器。
MFA 中间会话必须跨请求保留(start → send → verify 三步),硬依赖 Redis,
故 Redis 不可用时该步直接失败——captcha 有内存降级,MFA 没有(设计上不可降级)。
③ 修复(仅运维配置,不入库):
/opt/cpdaily-xiaozhushou/config.toml 的 host 由 "172.22.0.7" 改为容器名
"1Panel-redis-zDUv"(同属 likedream-net,容器名 DNS 稳定,不随 IP 漂移;
端口 6379 与密码 redis_4APk8B 本就对应此实例,无需改动)。
修改前已备份为 config.toml.bak.<时间戳>。
④ 验证:两个槽位(8884/8885)重启后,
- 启动日志不再出现 "Redis 连接失败(...connection refused)";
- 调用 /api/auth/captcha 后,Redis 相关警告行数由每次必现降为 0
(此前每次操作必打「验证码写入 Redis 失败,降级内存存储」)。
已确认 Redis 连通,未复测完整登录流程。
⑤ 本提交为空提交(--allow-empty):本次修复全部在宿主机运维配置,
无仓库代码变更,仅以此 commit 记录版本事件与排查结论。
v1.2.67
|
2026-09-18 21:28:43 +08:00 |
|
becomer
|
a3ec85f4d3
|
v1.2.66:扫码签到表单项顺序调整——经纬度/地址在前,二维码置于最后
ci/woodpecker/tag/build Pipeline was successful
扫码签到表单原顺序为「二维码ID → 经度 → 纬度 → 地址 → 照片 → 额外字段」,
与普通签到(定位在前)不一致。现调整为「经度 → 纬度 → 地址 → 照片 → 额外字段 → 二维码ID」,
定位字段顺序与普通签到对齐,二维码作为扫码任务特有项放在最后。
仅调整 parseScanTaskForm 中表单项的 append 顺序;fillScanSubmitForm 按 colName 查找字段,
不依赖顺序,提交逻辑不变。go build ./... 通过。
v1.2.66
|
2026-09-18 18:05:50 +08:00 |
|
becomer
|
ee466b253d
|
v1.2.65:修复扫码签到报错 empty String——补上经纬度/地址
ci/woodpecker/tag/build Pipeline was successful
① 问题:用户陈莎莎(昆明文理学院,任务 139「2027届毕业生体检签到」)扫码签到
始终报 "empty String"。
查日志:submitSign 响应 code=-1 message="empty String",即今日校园服务端返回的
校验错误(我们代码里无此字符串,仅原样透传)。
② 根因:该任务 signMode=2(扫码签到)。v1.2.40 把扫码签到提交体改为
「用 qrUuid 替代 signInstanceWid、不携带经纬度」,但实测服务端仍要求定位字段:
SubmitSign 的提交信封里 lat/lon 取 submitForm 的 latitude/longitude,
扫码分支被留空 → 提交外层 {"lon":"","lat":""} → 服务端把空串转数字抛
NumberFormatException: empty String。对照官方 qrsign 页面 JS(counselor-front),
其 submitSign 的 body 明确含 position 与 ...currentPosition(经纬度),
印证定位字段是必需的。历史上扫码签到从未成功过一次(成功记录均为普通定位签到)。
③ 修复(对齐普通签到):
- parseScanTaskForm 新增「经度/纬度/签到具体地址」三个表单项(lon/lat/address),
取值优先级与普通签到一致:signPlaceSelected[0] → 历史/详情的 longitude|latitude
→ 详情的 position|signAddress;抽为 extractScanPosition 复用。
- fillScanSubmitForm 提交体补上 longitude/latitude/position,
表单未提供时回退任务详情的学校默认坐标。
- 前端按 colName 渲染,新字段自动显示,无需改动。
④ 验证:go build ./... 、go vet ./... 、go test ./... 全过
(同步更新扫码任务的表单/提交体断言)。不检查部署。
v1.2.65
|
2026-09-18 18:00:41 +08:00 |
|
becomer
|
2effdbaaf9
|
v1.2.64:删除等操作的原生 confirm/alert 统一改为项目 AppDialog
ci/woodpecker/tag/build Pipeline was successful
① 问题:删除任务的确认框是浏览器自带弹窗,与全站统一样式不一致。
排查确认这是 v1.2.55「弹窗统一化」的遗留缺口——那次只扫描并迁移了手写
role="dialog" 元素(提交信息称「全项目手写 role=dialog 残留归零」),
而 confirm()/alert() 是浏览器原生调用、不产生 role="dialog" 元素,
故从未被纳入扫描范围。「归零」对 role="dialog" 属实,但原生弹窗不在其内。
② 全项目原生弹窗共 9 处 / 4 文件:
HomeView:删日志、删任务(confirm);保存失败 ×2、获取表单失败(alert);
TicketList:关闭工单、删除工单(confirm);
LeaveView:删日志(confirm);AdminAnnouncements:删公告(confirm)。
③ 修复:
- 新增 composables/useConfirm.ts:模块级单例服务(对齐既有 useMessage 风格),
confirmDialog()/alertDialog() 返回 Promise;重复调用时旧的以 false 结清,
避免 Promise 悬挂;respond 延迟 200ms 清空 state 以配合离场动画。
- 新增 components/ConfirmHost.vue:全站唯一弹窗宿主,挂到 App.vue,
z-index 取 z-[300](高于版本更新弹窗的 z-[200])。
视觉对齐现有居中弹窗(圆形图标底 + 标题 + 灰色说明 + 双按钮);
danger=true 时红色图标底 + destructive 确认按钮(--color-destructive #ef4444)。
- 9 处调用点改为 await confirmDialog/alertDialog;
删除类置 danger:true,关闭工单等非破坏性操作不置。
④ 验证(CDP 驱动真实 Chrome 实测,非仅静态检查):
- 弹窗为 role="dialog" 的项目组件,原生 confirm/alert 调用次数均为 0;
- 取消返回 false、确定返回 true、Esc 视为取消(false),按钮文案/颜色符合预期;
- 截图确认三种形态(危险确认/普通确认/单按钮提示)视觉与现有弹窗一致;
- 源码与构建产物双重扫描:代码级原生弹窗残留 0 处;
- vue-tsc --noEmit、npm run build 通过。不检查部署。
v1.2.64
|
2026-09-18 17:20:20 +08:00 |
|
becomer
|
e979e3cc9f
|
v1.2.63:修复二维码解析失败——改用两阶段多尺度扫描 + Otsu 兜底
ci/woodpecker/tag/build Pipeline was successful
① 问题:用户上传二维码报「未识别到二维码」,但同一张图用草料二维码能正常解析。
实测该图(1176x1568,斜拍屏幕、二维码区域约 674x674,周围有深色文字噪声):
jsQR 在原生分辨率与放大后均无法解码,仅在特定缩小档位可解。
② 根因:jsQR 使用固定的 8x8 分块自适应二值化,只有当"二维码模块边长"与块尺寸
量级相当时才能正确二值化,因此对缩放极其敏感——同一张图某档能解、相邻档即失效。
原实现只试 [1, 0.6, 0.4, 1.5] 四档,恰好只有 0.4 落在可解窗口内,属侥幸命中;
而不同浏览器/WebView 的 canvas 重采样算法不同,可解档位会整体漂移,
故出现"桌面偶尔能过、手机端始终失败"。实测在 Google Chrome 下将重采样设为
nearest 时,原实现即失败。
③ 修复(HomeView.vue 的 decodeQrInBrowser 重写):
- 多尺度细扫:按"目标长边"从 min(原图长边,1400) 起以 0.72 递减至 200,
覆盖连续可解窗口,不再依赖单档侥幸命中;
- 两阶段:阶段一逐档直接解码;阶段二对每档先做全局 Otsu 二值化再解码,
补足对比度差/斜拍屏幕的图片(阶段一失败改为 break 而非 return,
并只分配一半预算,确保 Otsu 兜底始终有机会执行);
- 时间预算 3000ms 封顶;逐档 await 让出主线程,避免"解析中"loading 卡死。
- 新增 loadImageFromFile / otsuBinarizeInPlace 辅助函数。
④ 验证(Google Chrome headless,用真实构建产物抽取解码函数运行):
- 用户原图 + 10 个扰动变体(旋转/亮度/模糊/噪声/重压缩/低分辨率)共 12 张全部解出正确 uuid;
- 2 张无二维码图片正确返回空;
- 四种重采样模式(high/medium/low/nearest)全部通过(原实现 nearest 失败);
- 真实 CPU 耗时:成功约 0.3–0.9s,失败(无二维码)约 4.6s 且全程逐档让出主线程;
- vue-tsc --noEmit、npm run build 通过。不检查部署。
v1.2.63
|
2026-09-18 16:57:27 +08:00 |
|
becomer
|
4ec3e55030
|
v1.2.62:二维码解析全面移到前端,移除后端解析接口与 gozxing 依赖
ci/woodpecker/tag/build Pipeline was successful
① 前端:取消二维码解析的后端降级。二维码一律由前端 jsQR 解码并提取 uuid;
识别失败直接提示用户换更清晰的图片,不再把图片回传后端。
② 后端:删除 POST /task/qr/decode 接口(handler.DecodeTaskQr)及其路由;
删除 qrcode.go 中所有图片解码相关代码(DecodeQRFromImage / decodeQRImage / scaleImage /
ResolveQrUuidFromContent / decodeQrTextToUuid)——后端不再接收二维码图片。
保留 ExtractQrUuid(纯正则文本提取),提交签到(fillScanSubmitForm)与历史值归一化仍在用。
③ 依赖:go mod tidy 移除 github.com/makiuchi-d/gozxing(不再被引用)。
golang.org/x/image 因验证码字体(captcha.go)仍在用,保留。
删除仅供已删测试使用的 internal/service/cpdaily/testdata/test_qr.png。
④ 前端 API:移除 auth.ts 的 decodeQr()。
⑤ 验证:go build / go vet / go test ./...、vue-tsc --noEmit、npm run build 全过。
v1.2.62
|
2026-09-18 16:40:15 +08:00 |
|
becomer
|
0bd354cfee
|
v1.2.61:二维码改前端解析(jsQR)+ 图片上传前压缩 + 修复 message 解构错误 + 上传 loading
ci/woodpecker/tag/build Pipeline was successful
① 修复 message 解构错误(导致「undefined is not an object (evaluating 't.warning')」):
useMessage() 返回的是 { toasts, success, error, info, warning, removeToast } 对象本身,
而 HomeView 与 LeaveView 写成了 const { message } = useMessage(),解构出的 message 恒为 undefined,
所有 message.xxx() 调用都会抛错。改为 const message = useMessage()(其余 18 个文件均为正确写法)。
② 二维码改为前端 jsQR 解析(问题 3、4):
上传二维码时不再走后端解析,直接在前端用 jsQR 从画布像素解码(多尺度尝试 + attemptBoth),
提取 uuid 存表;前端识别失败时降级调用后端 /task/qr/decode(gozxing 多尺度重试)兜底。
好处:免上传、免压缩、响应快;且能正确解析 https://...?uuid=xxx&isNeedExtra=0&schoolId=... 这类链接。
新增依赖 jsqr@^1.4.0。
③ 图片上传前前端压缩到 ≤300KB(问题 4):
签到照片等需上传 OBS 的图片,先经 canvas 逐步降质量/降尺寸压缩到 300KB 以下再上传,
避免大图(如 4032x3024)直传。二维码字段不走压缩(不保存图片,只存 uuid)。
④ 二维码上传增加 loading(问题 1):
原二维码分支从未设置 uploadingImage,故无 loading 效果。现统一走 uploadingImage + uploadingLabel
(解析中/压缩中/上传中),二维码与图片两种字段的 loading 块都补上阶段文案。
⑤ 验证:vue-tsc --noEmit、npm run build 通过。不检查部署。
v1.2.61
|
2026-09-18 16:36:37 +08:00 |
|
becomer
|
6e145f75e1
|
v1.2.60:修复签到照片可能以 base64 存入 formData
ci/woodpecker/tag/build Pipeline was successful
① 问题:签到照片(signImg)本应上传今日校园 OBS 后保存正式 URL,但实测存在 2.1MB 的 base64 记录(id=42)。
根因在前端 handleImageUpload:先把所选图片读成 dataURL 写入 content 作本地预览,上传 OBS 成功后才覆盖为
previewUrl;而以下任一情况都会把 base64 留在表单里并被保存:OBS 返回非 2xx(原代码直接 return 不回滚)、
获取预览 URL 失败(同样不回滚)、网络异常、以及用户在上传完成前就点了保存。
(后端 ExecuteTask 对 base64 有自动补传 OBS 的兜底,所以任务仍能成功,但表单里一直存着大字段。)
② 修复:
- handleImageUpload 各失败路径回滚 content 为原值(prevContent),不再残留 base64;
- 新增 guardBase64Images:保存前校验图片字段不得为 data: 开头(本地预览),
在 saveForm / saveEdit 两处拦截并提示,避免上传未完成时保存。
③ 数据清理(已执行,不可回滚):清空 id=133(二维码 base64,2.9MB)与 id=42(签到照片 base64,2.1MB)
的 form_data,让用户重新填写;全库已无 >50KB 的 formData 记录。
同时删除调试探针 backed/internal/tmpprobe/(含硬编码用户数据,未被任何代码引用)。
④ 验证:vue-tsc --noEmit、npm run build 通过;go build ./... 通过。
v1.2.60
|
2026-09-18 16:26:12 +08:00 |
|
becomer
|
879628d0eb
|
v1.2.59:修复首页接口传输 2.9MB(列表瘦身 + gzip)+ 二维码只存 uuid
ci/woodpecker/tag/build Pipeline was successful
① 根因:GET /api/task/list 每次返回约 2.9MB。ListTasks 用 Find 返回整行 CpTask(含 longtext 的 formData),
而历史数据里 formData 存过 base64 图片(id=133 的 qrCode 单条 2,959,961 字节,实为 4032x3024 现场照片),
且 nginx gzip_types 未覆盖 application/json,明文传输。该任务创建于 09-18 13:25,与“今天突然变慢”吻合。
实测:任务仅 62 条,form_data 合计 515 万字节;某 IP 单日拉取 53 次 ≈ 99MB。
② 列表瘦身:ListTasks 改返回轻量 DTO TaskListItem(不含 formData),并新增服务端预计算的 missingFields
(用已加载的 formData 计算,零额外 IO)。新增 GET /task/form-data/:id 返回该任务已保存表单,供编辑弹窗预填
(/task/form/:id 是从今日校园重拉模板会丢已填值,不能替代)。前端 executeTask 预检改用 missingFields,
openEditModal 改走新接口;TaskInfo.formData 改为可选。
③ 二维码只存 uuid:新增 POST /task/qr/decode,复用后端已有 gozxing 解析(不引入新库),
前端上传后仅保存 uuid(Prompt:中文名“二维码ID”,colName=uuid),不再写入整张 base64 图片。
关键修正:ExtractQrUuid 原先只认 32 位 hex 裸串,而链接里的 uuid 允许 [0-9a-zA-Z_-]+,
仅存裸 uuid 会导致非 hex 学校执行时解析失败 → 新增 bareTokenPattern(8~64 位 token)分支,
并让 fillScanSubmitForm 兼容 uuid/qrCode 双字段名(旧数据不受影响)。
④ gzip:在应用层加 fiber compress 中间件(nginx 由 1Panel 托管、跨 5 站点、不进版本库,不宜直改),
用 Next 排除 SSE 端点 /api/task/execute/*(流式响应不可缓冲)。
⑤ 验证:go build、go vet、go test ./...(新增裸 uuid/非 hex uuid/字段归一化用例)、vue-tsc --noEmit、npm run build 全过。
不清理历史 base64 数据(按要求暂不动)。
v1.2.59
|
2026-09-18 16:09:20 +08:00 |
|
becomer
|
8435095b4f
|
v1.2.58:改为真·蓝绿部署(消除部署期 502)+ 设备码按钮文案随绑定状态变化
ci/woodpecker/tag/build Pipeline was successful
① 部署方式重构为真正的蓝绿(修复 v1.2.55~57 每次部署约 3 分钟的 502 窗口):
根因:旧脚本是「翻到备用端口 → 1 秒后翻回 → 在活跃端口重启容器」,最终稳态只剩一个容器;且两次 nginx -s reload 仅隔 1 秒,openresty 未设 worker_shutdown_timeout(仅 keepalive_timeout 60),旧 worker 会继续持有连接并按旧配置往已删除的备用端口转发 → 502 持续到旧 worker 排空(实测 17:28:27~17:31:21,8885 Connection refused 86 次,8884 为 0)。
新方案:两个固定槽位(blue=8884 / green=8885)常驻。每次部署只更新「当前无流量的备用槽」→ 容器内健康检查 → nginx -t 校验 → 翻一次反代指向它 → 等旧 worker 全部退出;活跃槽容器保留作为上一版本供秒级回滚。因两端口始终都有活容器,旧 worker 无论是否排空都不可能出现 502。
另含:旧命名容器一次性收养(cpdaily-xiaozhushou → cpdaily-xiaozhushou-<端口>,避免下次部署端口冲突)、清理 cpdaily-blue 陈尸容器、反代配置切换前备份与 nginx -t 失败自动回滚、健康检查失败时不动反代。脚本已用 mock docker 干跑验证:两次连续部署正确轮换(8885→8884)、健康检查失败分支正确中止且不改反代。
② 个人中心设备码按钮:未绑定设备码时显示「立即绑定」,已绑定显示「重新绑定」(新增 hasDeviceCode computed)。
③ 验证:vue-tsc --noEmit、npm run build(含 postbuild-css)全过。前端改动 + CI 配置,无后端逻辑变更。
v1.2.58
|
2026-09-17 18:07:21 +08:00 |
|
becomer
|
200edd75bb
|
v1.2.57:设备绑定页文案与返回按手机端场景调整
ci/woodpecker/tag/build Pipeline was successful
① 面向手机端(主要客户群为手机用户)重写操作引导,三步简化为两步:第 1 步「保存二维码」(点击上方按钮保存二维码图片)、第 2 步「打开今日校园右上角扫一扫」(扫描二维码,绑定成功后自动跳转);删除原第 3 步「等待自动完成」(信息已并入第 2 步副文案)。
② 顶部说明由「请用今日校园 App 扫描下方二维码完成绑定」改为「请扫描二维码,绑定设备信息」。
③ 底部返回按钮改用 router.back()(原先写死 push('/profile'));因该页当前仅由个人中心「重新绑定」进入,back 即回个人中心,同时避免依赖硬编码路由。
④ 清理与手机端矛盾的文案(原「扫描电脑屏幕上的二维码」)。验证:vue-tsc --noEmit、npm run build 全过。纯前端改动,无后端变更。
v1.2.57
|
2026-09-17 17:27:56 +08:00 |
|
becomer
|
b39f57b879
|
v1.2.56:设备绑定页底部「退出登录」改为「返回」——点击回到个人中心
ci/woodpecker/tag/build Pipeline was successful
① 需求:绑定设备页(/bind-device)底部的「退出登录」按钮改为「返回」,点击回到个人中心。
② 改动:BindDeviceView.vue 底部按钮图标 LogOut→ArrowLeft、文案「退出登录」→「返回」、handleLogout()→handleBack()(router.push('/profile'))。用 push 固定目标而非 back(),因为本页由个人中心「重新绑定」进入(ProfileView.vue:136),返回目标明确。
③ 清理:移除 handleLogout 与 LogOut import(tsconfig 开启 noUnusedLocals,不清理会导致 vue-tsc 报错)。退出登录功能未丢失——个人中心 ProfileView.vue:467-475 保留独立退出入口;轮询/倒计时的清理仍由 onUnmounted 承担,导航离开即触发,无泄漏。
④ 验证:vue-tsc --noEmit、npm run build(含 postbuild-css)全过。纯前端改动,无后端变更。
v1.2.56
|
2026-09-17 17:16:00 +08:00 |
|
becomer
|
f20a6b57c1
|
v1.2.55:弹窗统一化——全站手写弹窗迁移到统一 AppDialog 组件 + 国产浏览器/微信内核兼容
ci/woodpecker/tag/build Pipeline was successful
① 弹窗统一化(核心):新增 AppDialog 组件(reka-ui Dialog 底座),把 13 个视图文件里 34 处手写弹窗全部迁移,全项目手写 role="dialog" 残留归零。
- 目的:以 Dialog 原语换取焦点捕获、Esc 关闭、body 滚动锁定、aria 语义与 Portal 渲染,同时保持与手写弹窗一致的视觉(面板基类 w-full/bg-white/flex-col/overflow-hidden/shadow-xl/max-h-[85vh])。
- 公开 API 与底座解耦:open / variant(sheet|center) / z-class / overlay-class / wrapper-class / panel-class / dismissable / fade / @update:open / 默认插槽。
- 逐处核对:阴影(原无阴影者显式传 shadow-none 抵消基类 shadow-xl、shadow-2xl 显式覆盖)、圆角(非基类值显式覆盖)、fade(原 <Transition name="fade"> 者传 :fade=true)、守卫(dismissable:LeaveView executing/applying、AdminCdks result 视图、AdminPendingSchools rejectSubmitting、HomeView「重新登录」原遮罩不可点故 false)、z-index(z-[60] 等)、定位层边距(wrapper-class px-4/6)。
- fade 实现说明:reka 的 Presence 只识别 CSS animation(读 computed animation-name 并监听节点自身 animationend),不认 Vue 的 class 过渡;故用 CSS animation + reka 输出的 data-state="open|closed" 实现,挂在 DismissableLayer 根节点(即 role="dialog" 节点)。实现在 fade 时额外给 content 根加 relative + 同层 z-class:opacity<1 会形成 z-index:0 的层叠上下文,否则内部 fixed 面板会被 overlay 盖住。
- 迁移固有行为差异:MobileLayout 公告面板失去 slide-up 上滑动画改为 fade;各弹窗新增 Esc 关闭能力(reka 默认);面板固定 overflow-hidden+max-h-[85vh](原无此二类的面板有微调)。非弹窗遮罩(AdminLayout 侧边栏抽屉、右侧滑出公告面板;GuideOverlay 引导层)按判断保留未迁移。
② 国产浏览器/微信内核兼容(chrome78+):
- vite build.target 降到 chrome78(微信 XWEB 常见下限),旧的默认 target 会产出 Chrome 80 以下无法解析的新语法导致白屏。
- 新增 scripts/postbuild-css.mjs 并接入 build:解包 :where、降级 oklch 颜色、剥离 @layer,产出对老内核可用的 CSS;含兜底校验(残留 @layer/oklch/:where 直接失败)。
- index.html 内联补齐 Object.hasOwn(Chrome 93+,reka-ui 与 @vueuse/shared 内部调用;esbuild 只降语法不降运行时 API)。
③ 收尾修复既有问题:AdminUsers 删除从未使用的 Card 死 import;AdminCdks 修正 copiedResultIdx/resultCopiedIdx 变量名不一致(导致结果视图复制高亮失效并抛 ReferenceError);MobileLayout 原 .slide-up-leave-to 误用 transition(随迁移一并移除,问题消失)。
④ 验证:vue-tsc --noEmit、vite build(含 postbuild-css)全过;headless Chrome 实测整站启动渲染正常;全项目残留扫描确认无遗留手写弹窗。
⑤ 本次为纯前端改动(含 reka-ui 依赖),不含后端变更。
v1.2.55
|
2026-09-17 16:05:11 +08:00 |
|
becomer
|
55eff6e80a
|
v1.2.54:修复请假附件区被隐藏——根因:旧套学校 enableAttach 恒为 0,不能作为显示/放行条件——① 实测复现:陈莎莎(ysdwl 昆明文理学院)名下 6 个请假类型,queryLeaveTypeCfg 全部返回 enableAttach=0、attachmentNumberLimit=4(学校实际允许传 4 张,app 内亦显示为可选附件),而 v1.2.53 前端显示条件为 enableAttach===1,导致所有旧套学校附件区被隐藏;多校只读采样(马鞍山/兰州财经/安徽师大/芜湖职大/蚌埠医科/新疆师大/矿大)确认旧套 enableAttach 全为 0、limit 全为 4,属该字段在旧套链路的语义问题而非个例;② 判定修正:可传附件 = enableAttach===1 或 attachmentNumberLimit>0(前端新增 applyAttachVisible;旧套服务端校验同步改为「两者都表明不可用才拒绝」);上限仍取 min(4, 学校值);③ 新套侧补漏:leaveNewToLeaveTypes 在 isVisibledEvidenceFile=false 时不再带出 evidenceFileCountMax(否则前端按「上限>0」会误显示上传入口),仅可见时带出;④ 新增回归测试 TestLeaveNewToLeaveTypesAttachGating(可见/不可见类型分别断言开关与上限);go build、go vet、go test -count=1 ./...、vue-tsc --noEmit、npm run build 全过;⑤ 全流程只读实测(未调用任何提交接口),真实提交由用户自测
ci/woodpecker/tag/build Pipeline was successful
v1.2.54
|
2026-09-16 18:02:35 +08:00 |
|
becomer
|
b436781d7d
|
v1.2.53:请假申请附件支持多图上传(最多 4 张)——用户对比老版本发现「请假那里没有上传图片」——① 现状澄清:上传入口本就存在但被学校开关隐藏(v-if=enableAttach===1),且只能传 1 张;本版新增「附件」字段并支持多图,保留学校开关(用户确认);② 数量上限:min(4, 学校配置)——新增 LeaveMaxAttachCount(未配置/超 4 均按 4,否则取学校值),旧套取 queryLeaveTypeCfg.attachmentNumberLimit、新套取 getLeaveTypeDetail.evidenceFileCountMax;③ 协议确认:两套提交体字段本就是数组(旧套 applyAttach=[{type:1,url}]、新套 attachs=[{attachType:1,attachUrl,obsKey,...}]),多图为自然扩展,无需新增接口;上传/预览复用现有 /api/task/upload-policy、/api/task/preview-url(已含新旧套分流),每张图走一遍链路;④ 后端:LeaveApplyParams.ImageURL→ImageURLs、LeaveNewApplyParams.ImageURL/ImageKey→ImageURLs(每张 obsKey 由 URL 推导);请求体新增 imageUrls[],保留旧 imageUrl 字段做兼容合并(保序去重、剔除空串,避免旧缓存前端附件被静默丢弃);服务端复核:旧套校验 enableAttach 开关与数量上限,新套校验 isVisibledEvidenceFile 开关、数量上限、isRequiredEvidenceFile 必填与 evidenceFileCountMini 张数下限;⑤ 前端:附件区改多图网格(缩略图+单张 X 移除+未达上限显示上传方块),label 显示「最多 N 张,已传 x/N」;多选逐张顺序上传并复用 uploadImageToObs;切换类型时不支持附件则清空、上限变小则截断;提交前复核开关与上限;⑥ 销假保持单图不动(未在本次范围内);⑦ 新增测试:service 层多图断言(旧套 2 张 applyAttach、新套 2 张 attachs 逐张 obsKey)、LeaveMaxAttachCount 边界(0/-1/2/4/9)、空串剔除、handler mergeApplyImageURLs 7 用例(去重/追加/空白裁剪/全空);go build、go vet、go test -count=1 ./...、vue-tsc --noEmit、npm run build 全过
ci/woodpecker/tag/build Pipeline was successful
v1.2.53
|
2026-09-16 17:48:17 +08:00 |
|
becomer
|
ac743eda88
|
v1.2.52:执行日志软删除改造——用户删除仅标记 deleted_at(前台隐藏、任务状态不受影响),后台统计与明细计入已删除(标注「已删除」)——① 动机:日志删除原为物理删除,会连带从后台统计中消失,导致「已执行累计/今日统计」凭空减少,且删除当天成功日志会让任务被跨天重置逻辑误判为未执行、出现重复打卡;② 模型:CpTaskLog / CpLeaveLog 增加 DeletedAt gorm.DeletedAt(带索引),AutoMigrate 自动加列,存量数据 deleted_at 为 NULL 视为未删除,无需数据迁移;③ 前台:GORM 默认作用域自动过滤软删除,用户端日志列表(/task/logs、/leave/logs)与删除接口(DB.Delete 自动转为 UPDATE deleted_at)无需改动即符合预期;④ 任务完成状态:ListTasks 的「当天是否存在成功日志」跨天重置查询改用 Unscoped,删除当天日志不再触发状态回退(否则会重复打卡);⑤ 后台统计:已执行累计、今日状态、今日统计卡片(执行/成功总数)全部改用 Unscoped 并并入请假/销假(cp_leave_logs),口径统一为「签到+查寝+请假+销假」;⑥ 后台明细:AdminListTaskLogs 由单表查询改为两表 UNION(同一条常量 adminLogUnionSQL,事务探针复用),新增 deleted(已删除标注)与 source(1=任务 2=请假销假)字段,请假/销假映射 task_type=leave_apply/leave_report;⑦ 修复 UNION 真实报错:两表分属不同时期建表,字符串列 collation 不一致(cp_task_logs=utf8mb4_unicode_ci / cp_leave_logs=utf8mb4_general_ci),直接 UNION 报 MySQL 1271 Illegal mix of collations,已对请假侧字符串列显式 COLLATE utf8mb4_unicode_ci;⑧ 新增事务级探针 TestSoftDeleteProbe(默认跳过,PROBE_DB=1 时连真实库执行并整体回滚,不改动任何持久数据):覆盖前台隐藏、状态不受影响、后台计入、UNION 标注/deleted/source/success 过滤分支;⑨ 前端:AdminUsers.vue 日志明细类型标签补「请假/销假」、已删除记录追加灰色「已删除」徽标、列表 key 由 id 改为 source-id(两表 id 独立自增会撞键),api/admin.ts 类型同步;⑩ 验证:go build、go vet、go test ./...、PROBE_DB=1 事务探针、vue-tsc --noEmit、npm run build 全过
ci/woodpecker/tag/build Pipeline was successful
v1.2.52
|
2026-09-15 18:38:16 +08:00 |
|
becomer
|
53cbedeb95
|
v1.2.51:泰州学院(tzxy)等新套学校请假/销假适配——新增 wec-counselor-leave-new-apps 全链路 + 按学校应用自动分流
ci/woodpecker/tag/build Pipeline was successful
① 根因与定位:tzxy 的旧前缀 wec-counselor-leave-apps 被网关整体拦截返回 HTTP 420「产品已到期」,
但该校实际使用的是新一代「请销假」应用 wec-counselor-leave-new-apps/stu/mobile/index.html
(由辅导猫首页 getAppInfos 应用列表确认;实际接口实测全部正常)。
新旧两套是完全不同的 API:新套明文 JSON + CpdailyClientType、类型用 typeWid、
审批流/抄送独立接口;旧套 bodyString 加密 + Cpdaily-Extension 签名、类型用 code。
② 自动分流:新增 handler/leave_flavor.go——辅导猫 getAppInfos 探测本校请假应用 openUrl
判定 new/old,探测失败用新套 ping 兜底,结果按学校缓存 30 分钟;
所有请假链路(类型/审批人/待销假列表/销假审批人/销假执行/请假执行/上传策略/预览)按 flavor 分发。
③ 新套实现(cpdaily/leave_new.go):queryLeaveType(分页拉全)、getLeaveTypeDetail(权威开关)、
getApproveFlowList(审批人+flowData)、getCcSearchList/getLastCcObjects(抄送人)、
/stu/leave/apply(明文 JSON 提交)、/stu/leave/list、reportList(规定销假位置)、
getReportApprovers(销假确认人)、queryLeaveDetail、/stu/leave/report(销假提交)、
OBS getUploadPolicy/previewAttachment(COS 风格,obsKey 从 URL 推导与官方前端一致)。
④ 契约翻译:新套数据在 handler 层翻译为现有前端契约(LeaveType/LeaveItem/LeaveApprover),
前端零结构改动;新增 statusText 权威状态文案(新套 1审批中/2即将休假/3休假中/4销假逾期/
5销假确认中/6销假已驳回/7已完成/8已驳回/9已撤销/10退回重填),前端优先展示、旧套回退本地映射;
pending-list 仅对 status 3/4 补拉详情判断 canReport(reportMode 1/2),附图/审批人开关。
⑤ 服务端复核:新套请假执行重新拉类型详情校验(原因必填/字数/证明材料)、审批流候选
(userWid 匹配完整对象、无候选不阻止)、抄送人完整对象解析、离校语义(isLeaveOut 强制时以类型为准);
销假执行复核 canReport 状态与确认人。
⑥ 测试:新增 leave_new_test.go 7 个测试(obsKey 推导/canReport/状态文案/销假表单/请假表单
离校与不离校两分支/响应码解析);go build/go vet/go test 全过;前端 vue-tsc + npm run build 通过;
端到端只读实测 tzxy:flavor=new、5 类型、审批人张文烨、2 销假位置、销假确认人、详情与 OBS 策略全部正常。
v1.2.51
|
2026-09-15 13:34:05 +08:00 |
|
becomer
|
42d3b332a5
|
v1.2.50:辽宁工程技术大学(lntu)登录适配——强制走 CAS + 滑块验证码——① 根因:平台接口返回 joinType=CLOUD(IsIap=true),但网关 /iap/doLogin 不存在(HTTP 405),IAP 账密提交必然失败并返回 FAIL_UPNOTMATCH("用户名或密码不匹配",误导用户以为密码错误);真实链路为 fdm.lntu.edu.cn/iap 302 → 学校 CAS(authserver.lntu.edu.cn/authserver/login)→ 账密 + 验证码 → 回调 /iap/loginSuccess?sessionToken=...&ticket=... 建立网关会话(cookie 落在 fdm.lntu.edu.cn,与 wec-* 任务接口同域);② 修复:新增 login_lntu.go 的 lntuCasLogin(同 xcu 模式,用 /iap 网关入口覆盖 ampUrl 后走通用 CAS 流程),CpdailyLogin 中对 LntuSchoolId 特判强制走 CAS;③ 验证码类型:抓取登录页 JS 确认 captchaSwitch="2" 且 startLogin 走 createSliderCaptcha()(wisedu 定制滑块,非图形验证码),协议与华侨大学完全同源(openSliderCaptcha.htl 取图、sign=AES(JSON.stringify({canvasLength,moveLength,tracks}), 小图 base64 末16字节) 提交 verifySliderCaptcha.htl、errorCode==1 通过),故仅需将 lntu 加入 sliderIds 配置即可复用现有滑块实现(config.toml.example 同步更新);④ 端到端实测:真实待适配账号两次登录均一次通过(gapX=92/149 首候选 offset=0 直接命中 errorCode=1),CAS 302 → 6 cookies(CASTGC/JSESSIONID/MOD_AUTH_CAS/happyVoyage/route/platformMultilingual)→ FetchUserProfile 会话有效;⑤ 按用户要求回滚此前 LoginUrl(忘记密码地址)改动——原地址打不开是因为走了 IAP 入口,改走 CAS 后浏览器可直接打开,无需特殊处理;⑥ 新增 login_lntu_test.go(入口计算 7 用例 + ampUrl 覆盖 + 协议常量),go test ./...、go vet 全过
ci/woodpecker/tag/build Pipeline was successful
v1.2.50
|
2026-09-14 23:12:06 +08:00 |
|
becomer
|
021ac7f7bd
|
v1.2.49:请假审批人「候选为空可不选」+ 新增抄送人(非必填,候选取自审批人列表)——① 审批人校验放宽(实测驱动):ysdwl「大四学生请假」needApproval=1 但审批节点(政工队伍应用管理员)未配置人员,obtainApproveFlow 返回空列表,旧逻辑强制必选导致用户无法提交;现前端在候选为空时不显示必选星号、提示「该校未配置审批人,可不选」,校验仅在有候选时要求选择;后端 execute 校验同步改为「needApproval=1 且候选非空才必填」(服务端自行查候选,避免前端被绕过),候选为空时记录日志放行;② 抄送人功能:ccInfo 从硬编码空结构改为按用户选择构造(BuildLeaveCcInfo),按 userWid 去重、按 userId 是否纯数字分组 teachers/students(personType 0/1,官方结构含 name2Id/dept/sex/teacherName 字段);前端抄送人区显示条件与 needApproval 解耦(候选同源 obtainApproveFlow,类型+时间齐全即加载),蓝色复选框多选、非必填,类型/时间变化后自动清理失效选择;③ 静默降级:候选加载失败不再弹错(避免每次切换类型红字打扰),需审批时提交由后端复核并给明确提示;④ 新增 TestBuildLeaveCcInfo 覆盖学生/教师分组、空列表、表单携带;go test ./...、go vet、vue-tsc、npm run build 全过
ci/woodpecker/tag/build Pipeline was successful
v1.2.49
|
2026-09-14 14:47:26 +08:00 |
|
becomer
|
b75e1f9bd9
|
v1.2.48:请/销假入口卡片副标题改为「异地一键请假销假,自行设置位置」——原文案「提交请假申请,或返校后一键销假」仅描述操作流程,未体现可异地执行与手动指定位置的核心能力;新文案同时覆盖请假与销假两个按钮(位置在按钮上方,两按钮共用),突出「异地」与「自行设置位置」卖点。vue-tsc、npm run build 通过
ci/woodpecker/tag/build Pipeline was successful
v1.2.48
|
2026-09-14 12:57:22 +08:00 |
|