Skip to content

毅行后端#45

Open
sqb550 wants to merge 113 commits intomainfrom
dev
Open

毅行后端#45
sqb550 wants to merge 113 commits intomainfrom
dev

Conversation

@sqb550
Copy link
Copy Markdown
Collaborator

@sqb550 sqb550 commented Mar 30, 2026

No description provided.

Penryn and others added 30 commits February 19, 2026 21:12
Signed-off-by: Penryn <15158052130@163.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1.个人信息包含了id
2.用户端对外统一字符串枚举不是数字
3.字段命名统一了一下:member_type,team_role
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

本 PR 引入“毅行/Walk-Server”后端的整体骨架与核心业务模块(用户端、管理员端、Dashboard),并补充了数据层(GORM Gen model/query + repo)、缓存层(Redis cache/lock)、部署与建表脚本,以支持报名、组队、打卡与大盘统计等功能闭环。

Changes:

  • 新增 HTTP 路由注册与应用 Boot/Command/Cron 启动框架,接入 Gin + mygo 生态组件(session/jwt/redis/db/log)。
  • 新增用户/管理员/Dashboard API 与 repo/cache 层实现,提供报名、组队、打卡、队伍筛选与统计查询等接口。
  • 新增数据库建表 SQL、代码生成入口、基础部署(Dockerfile、Loki/Grafana/Alloy)与说明文档。

Reviewed changes

Copilot reviewed 92 out of 97 changed files in this pull request and generated 14 comments.

Show a summary per file
File Description
register/route.go 注册 Gin 路由分组与中间件(/admin、/user、/dashboard)
register/generate/generate.go generate 模块 Boot 占位
register/cron.go Cron 注册占位
register/cmd.go Cobra 命令注册(server/cron)
register/boot.go 应用 BootList:日志、DB、Redis、JWT、Lock、业务配置加载等
middleware/checkPrem.go Dashboard 管理员权限校验中间件
middleware/auth.go 用户 JWT 鉴权中间件封装
middleware/.gitkeep 目录占位
main.go 应用入口:启动 HTTP Server + 伴生 cron
go.mod Go module 与依赖声明
docs/dashboard-pr-overview.md Dashboard 分层/缓存策略说明文档
deploy/sql/wrong_route_records.sql 新增 wrong_route_records 表
deploy/sql/teams.sql 新增 teams 表结构
deploy/sql/routes.sql 新增 routes 表结构
deploy/sql/route_edge.sql 新增 route_edges 表结构
deploy/sql/points.sql 新增 points 表结构
deploy/sql/peoples.sql 新增 peoples 表结构
deploy/sql/checkins.sql 新增 checkins 表结构
deploy/sql/admins.sql 新增 admins 表结构
deploy/loki/docker-compose.yaml Loki + Alloy + Grafana 本地观测栈
deploy/loki/conf/loki-config.yaml Loki 配置
deploy/loki/conf/alloy-config.alloy Alloy 采集日志并写入 Loki
dao/repo/route.go 路线统计/点位/路段聚合查询 repo
dao/repo/people.go 人员 repo(含按 OpenID 缓存)
dao/repo/admin.go 管理员 repo(含 session 反查 + 缓存)
dao/query/gen.go GORM Gen 生成的 Query 入口
dao/model/wrong_route_records.gen.go wrong_route_records model
dao/model/teams.gen.go teams model
dao/model/routes.gen.go routes model
dao/model/route_edges.gen.go route_edges model
dao/model/points.gen.go points model
dao/model/peoples.gen.go peoples model
dao/model/checkins.gen.go checkins model
dao/model/admins.gen.go admins model
dao/cache/team/team.go 队伍缓存 + 筛选缓存 + 分布式锁封装
dao/cache/route/route.go 路线/统计/点位/路段缓存封装
dao/cache/people/people.go 人员缓存封装
dao/cache/admin/admin.go 管理员缓存封装
dao/cache/.gitkeep 目录占位
cron/.gitkeep 目录占位
conf/config.example.yaml 配置模板(db/redis/session/jwt/biz 等)
comm/util.go JWT token 生成、AES 加解密、时间判断工具
comm/password.go bcrypt hash/verify
comm/lock.go 基于 redsync 的队伍互斥锁
comm/errors.go comm 包占位
comm/enum.go 枚举常量与解析/格式化函数
comm/config.go biz 配置结构体
comm/code.go 统一错误码定义
cmd/gen/generate.go GORM Gen 代码生成入口
api/user/wechatLogin.go 微信登录换取 OpenID 并发 token
api/user/userModify.go 用户可编辑信息更新
api/user/userInfo.go 用户信息 + 队伍信息查询
api/user/teamUpdate.go 队长修改队伍信息
api/user/teamLeave.go 队员退出队伍(事务+并发控制)
api/user/teamJoin.go 加入队伍(事务+并发控制)
api/user/teamInfo.go 获取当前队伍与成员信息
api/user/teamDisband.go 队长解散队伍
api/user/teamCreate.go 创建队伍
api/user/teamCommon.go 队伍通用结构与辅助函数
api/user/registerTeacher.go 教职工报名
api/user/registerStudent.go 学生报名
api/user/registerCommon.go 报名通用逻辑(参数校验+去重)
api/user/registerAlumnus.go 校友报名
api/dashboard/teams/team.go Dashboard 队伍详情(含缓存)
api/dashboard/teams/lost.go 设置队伍失联状态(含锁/缓存失效)
api/dashboard/teams/filter.go Dashboard 队伍筛选(含缓存/分页游标)
api/dashboard/stats/route.go 单路线详细统计(含缓存)
api/dashboard/stats/all.go 全路线统计聚合(含缓存)
api/dashboard/segment.go 路段人数统计(含缓存)
api/dashboard/permission.go 查询当前管理员权限信息
api/dashboard/overview.go 总览统计(按校区,含缓存)
api/dashboard/checkpoint.go 点位详情(已到/未到人数,含缓存)
api/admin/updateUser.go 管理员更新人员状态(事务+锁)
api/admin/updateTeam.go 队伍打卡/错路处理/写入记录
api/admin/regroup.go 重组队伍(事务)
api/admin/registerAdmin.go 管理员注册(测试用途)
api/admin/markTeamViolation.go 标记队伍违规
api/admin/getUserInfoScan.go 扫码查询人员信息
api/admin/getUserInfo.go 按 ID 查询人员信息
api/admin/getTeamStatus.go 获取队伍状态 + 成员状态
api/admin/confirmDestination.go 终点确认(更新队伍/成员状态)
api/admin/bindCode.go 绑定队伍签到码并推进状态
api/admin/authAdmin.go 管理员登录(session 写入)
README.md 项目说明、目录结构、启动与生成说明
Dockerfile 多阶段构建镜像
.gitignore 忽略配置、日志、生成文件等
.dockerignore Docker 构建忽略文件

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +64 to +77
team, err := teamRepo.FindTeamByID(ctx, h.Request.TeamID)
if err != nil {
return comm.CodeDatabaseError
}
if team == nil {
return comm.CodeDataNotFound
}
if team.Password != h.Request.Password {
return comm.CodePasswordWrong
}
if team.Submit != 0 {
return comm.CodeTeamSubmitted
}
maxTeamSize := 6
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里用 team.Password != h.Request.Password 做明文比较,配合 teams.password 明文存储会带来密码泄露风险。建议改为存储密码哈希并使用安全的 hash compare(bcrypt/argon2),同时考虑对密码尝试做限流以降低暴力破解风险。

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +3
-- Active: 1773747872536@@127.0.0.1@3306@jh_db
CREATE TABLE `peoples` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

文件顶部的 -- Active: ... 看起来是本地 SQL 客户端生成的连接标记,不属于建表脚本内容,容易污染迁移/回放。建议移除这类本地工具注释,保持 SQL 可重复执行且环境无关。

Copilot uses AI. Check for mistakes.
Comment on lines +35 to +41
// TeamStatus枚举
const (
TeamStatusNotStart = "notStart"
TeamStatusInProgress = "inProgress"
TeamStatusCompleted = "completed"
TeamStatusWithDrawn = "withDrawn"
)
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TeamStatusWithDrawn 的值是 withDrawn,但 teams.sql 的字段注释里使用的是 withdrawn。如果数据库里已有/将写入 withdrawn(或其他模块按注释实现),会造成状态判断与展示不一致。建议统一团队下撤状态的枚举值(推荐全小写 withdrawn,与 people.walk_status 一致),并同步更新前后端与数据库注释。

Copilot uses AI. Check for mistakes.
@@ -0,0 +1,10 @@
package generate

// 用于生成和注册生成模块的启动函数,请不要在此目录下防止自定义的业务代码
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

注释里“请不要在此目录下防止自定义的业务代码”里“防止”应为“放置”,避免歧义。

Copilot uses AI. Check for mistakes.

## 配置说明

配置模板见 [config.example.yaml]。
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

README 中“配置模板见 [config.example.yaml]”的引用路径不正确:仓库内文件在 conf/config.example.yaml,而这里的 Markdown 引用也未定义链接目标,读者点不开/找不到。建议改成明确路径(例如 conf/config.example.yaml)并使用可点击的相对链接。

Suggested change
配置模板见 [config.example.yaml]
配置模板见 [`conf/config.example.yaml`](conf/config.example.yaml)

Copilot uses AI. Check for mistakes.
Comment on lines +238 to +245
if err := u.handlePointCheckin(ctx, team, adminID, pointName); err != nil {
return nil, err
}

if routeEdge == nil || routeEdge.PrevPointName != team.PrevPointName {
return &routePointCheckinResult{code: &comm.CodePrevPointInvalid}, nil
}

Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

handleRoutePointCheckin 里在校验 routeEdge.PrevPointName 是否与 team.PrevPointName 匹配之前,就先写入了打卡(更新 prev_point_name + 插入 checkin)。当校验失败返回 CodePrevPointInvalid 时,数据库已经产生了副作用,会导致队伍状态/打卡记录被错误推进。建议将前序点位校验放在任何写操作之前,或把校验+写入放在同一个事务里并在校验失败时回滚。

Suggested change
if err := u.handlePointCheckin(ctx, team, adminID, pointName); err != nil {
return nil, err
}
if routeEdge == nil || routeEdge.PrevPointName != team.PrevPointName {
return &routePointCheckinResult{code: &comm.CodePrevPointInvalid}, nil
}
if routeEdge == nil || routeEdge.PrevPointName != team.PrevPointName {
return &routePointCheckinResult{code: &comm.CodePrevPointInvalid}, nil
}
if err := u.handlePointCheckin(ctx, team, adminID, pointName); err != nil {
return nil, err
}

Copilot uses AI. Check for mistakes.
Comment on lines +147 to +152
// 转换为累计经过人数:例如原始(0,1)会返回为(1,1)。
totalPassed := 0
for i := len(r.Response.PointStats) - 1; i >= 0; i-- {
totalPassed += r.Response.PointStats[i].PassedCount
r.Response.PointStats[i].PassedCount = totalPassed
}
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里把每个点位的 PassedCount 做了“从终点向前累加”,但上游的 ListRoutePointPassedCounts 统计的是“在该点位打过卡的总人数”(每支队伍会在多个点位产生 checkin)。这样累加会对同一批人重复计数,导致数值严重偏大。建议改为按“队伍当前/最大到达点位序号”来计算每个点位的累计到达人数(例如基于 teams.prev_point_name 或按 checkins 求每队最大 seq_order),而不是对点位打卡人数做累加。

Suggested change
// 转换为累计经过人数:例如原始(0,1)会返回为(1,1)。
totalPassed := 0
for i := len(r.Response.PointStats) - 1; i >= 0; i-- {
totalPassed += r.Response.PointStats[i].PassedCount
r.Response.PointStats[i].PassedCount = totalPassed
}
// 保持每个点位的 PassedCount 为“在该点位打过卡的总人数”,避免在多个点位重复累计同一批人。

Copilot uses AI. Check for mistakes.
Comment on lines +100 to +118
func NeedPerm(minPerm string) gin.HandlerFunc {
minPerm = strings.ToLower(strings.TrimSpace(minPerm))
return func(ctx *gin.Context) {
admin, ok := repo.GetAdminInfo(ctx)
if !ok {
return
}

currentRank, currentOK := getPermRank(admin.Permission)
minRank, minOK := getPermRank(minPerm)

if !currentOK || !minOK {
reply.Fail(ctx, comm.CodeMiddlewareServiceError)
return
}

if currentRank > minRank {
reply.Fail(ctx, comm.CodePermissionDenied)
return
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NeedPermminPerm 做了 trim+lowercase,但 admin.Permission 未做任何规范化就用于查表。管理员权限如果包含大小写差异或前后空格(例如注册/导入时写入了 Internal),这里会被当成未知权限并返回中间件错误码。建议对 admin.Permission 同样做 strings.ToLower(strings.TrimSpace(...)) 后再参与 getPermRank 判断。

Copilot uses AI. Check for mistakes.
Comment on lines +27 to +31
adminGroup := r.Group("/admin")
{
adminGroup.POST("/register", api.RegisterAdminHandler())
adminGroup.POST("/auth", api.AuthAdminHandler())

Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/admin/register 当前是无需登录即可访问的公开接口,这会允许任意人创建管理员账号(即使代码注释写“测试”,也很容易被误部署到生产)。建议至少在路由层加权限/环境开关(仅 dev/test 暴露),或要求已登录的 super 管理员才能调用。

Copilot uses AI. Check for mistakes.
Comment on lines +90 to +103
team := &model.Team{
Name: teamName,
Num: 1,
Password: h.Request.Password,
Slogan: h.Request.Slogan,
AllowMatch: boolToInt8(h.Request.AllowMatch),
Captain: openID,
RouteName: h.Request.RouteName,
Submit: 0,
Status: comm.TeamStatusNotStart,
Code: "",
IsLost: 0,
}
if errTx := txTeamRepo.Create(ctx, team); errTx != nil {
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

队伍加入密码在这里以明文写入 teams.password(并且后续加入时直接做明文比较)。这会导致数据库泄露时直接暴露所有队伍密码,也不利于审计合规。建议像管理员密码一样使用 bcrypt/argon2 等哈希存储,并在校验时用 hash compare;同时避免在日志/响应中回传密码。

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants