feat: PhotoWall 毕业照存储系统初始版本

后端: Go(Gin+GORM+MySQL+Redis)
- 17张业务表(yz_pw_前缀), 自动迁移
- JWT认证(3小时) + 盐+MD5密码 + 图形验证码
- 班级CRUD/加入(8人姓名验证/邀请码)/审核/30天自动清理
- 系统配置(16项)/菜单管理(动态路由)/数据统计
- 文件MD5去重/数据隔离/账号封禁/敏感词DFA检测

前端: Vue3+Vite+Element Plus+Less+ECharts+FontAwesome
- 动态路由(数据库菜单驱动)
- 蓝白配色, H5响应式
- 登录/注册/忘记密码/个人中心
- 班级创建向导/详情/加入/列表
- 管理后台: 审核/配置/菜单/统计/用户/敏感词

数据库: MySQL 10.31.100.3:3306/photowall
管理员: hero920103 / 920103
This commit is contained in:
hero920103
2026-09-03 05:55:23 +08:00
commit 57d9866dab
82 changed files with 12668 additions and 0 deletions
+169
View File
@@ -0,0 +1,169 @@
package router
import (
"photowall/internal/handler"
"photowall/internal/middleware"
"photowall/pkg/jwt"
"github.com/gin-gonic/gin"
)
// Deps 所有 handler 依赖
type Deps struct {
Auth *handler.AuthHandler
User *handler.UserHandler
Region *handler.RegionHandler
School *handler.SchoolHandler
Class *handler.ClassHandler
Admin *handler.AdminHandler
Upload *handler.UploadHandler
Config *handler.ConfigHandler
Menu *handler.MenuHandler
}
func Register(r *gin.Engine, jm *jwt.Manager, deps *Deps) {
// CORS 中间件
r.Use(corsMiddleware())
// 静态文件:上传的图片
r.Static("/uploads", "uploads")
api := r.Group("/api")
// ---- 公开路由 ----
public := api.Group("")
{
// 图形验证码
public.GET("/captcha", deps.Auth.Captcha)
// 认证
public.POST("/auth/register", deps.Auth.Register)
public.POST("/auth/login", deps.Auth.Login)
public.POST("/auth/forgot-password", deps.Auth.ForgotPassword)
public.POST("/auth/reset-password", deps.Auth.ResetPassword)
public.POST("/auth/send-sms", deps.Auth.SendSms)
public.POST("/auth/wechat-login", deps.Auth.WechatLogin)
public.GET("/auth/wechat-qrcode", deps.Auth.WechatQrcode)
// 地区(公开只读)
public.GET("/regions", deps.Region.List)
public.GET("/regions/countries", deps.Region.Countries)
public.GET("/regions/search", deps.Region.Search)
public.GET("/regions/:id/path", deps.Region.Path)
// 学校(公开只读)
public.GET("/schools", deps.School.List)
public.GET("/schools/:id", deps.School.Get)
public.GET("/schools/:id/colleges", deps.School.ListColleges)
// 班级搜索(公开,仅返回 active)
public.GET("/classes/search", deps.Class.Search)
}
// ---- 需要登录的路由 ----
auth := api.Group("")
auth.Use(middleware.Auth(jm))
{
// 当前用户
auth.GET("/auth/me", deps.Auth.Me)
// 动态路由:获取当前用户菜单
auth.GET("/menus", deps.Menu.GetUserMenus)
// 用户资料
auth.GET("/user/profile", deps.User.Profile)
auth.PUT("/user/profile", deps.User.UpdateProfile)
auth.GET("/user/has-education", deps.User.HasEducation)
auth.GET("/user/:id", deps.User.PublicProfile)
// 联系方式
auth.POST("/user/contacts", deps.User.AddContact)
auth.PUT("/user/contacts/:id", deps.User.UpdateContact)
auth.DELETE("/user/contacts/:id", deps.User.DeleteContact)
// 学习履历
auth.POST("/user/education", deps.User.AddEducation)
auth.PUT("/user/education/:id", deps.User.UpdateEducation)
auth.DELETE("/user/education/:id", deps.User.DeleteEducation)
// 学校/学院(登录用户可创建)
auth.POST("/schools", deps.School.Create)
auth.PUT("/schools/:id", deps.School.Update)
auth.DELETE("/schools/:id", deps.School.Delete)
auth.POST("/schools/:id/colleges", deps.School.CreateCollege)
auth.PUT("/colleges/:id", deps.School.UpdateCollege)
auth.DELETE("/colleges/:id", deps.School.DeleteCollege)
// 班级
auth.POST("/classes", deps.Class.Create)
auth.GET("/classes/mine", deps.Class.Mine)
auth.GET("/classes/:id", deps.Class.Detail)
auth.POST("/classes/:id/resubmit", deps.Class.Resubmit)
// 加入班级
auth.POST("/classes/join/search", deps.Class.JoinBySearch)
auth.POST("/classes/join/invite", deps.Class.JoinByInvitation)
// 邀请码(管理员)
auth.POST("/classes/:id/invitation", deps.Class.CreateInvitation)
auth.GET("/classes/:id/invitations", deps.Class.ListInvitations)
auth.DELETE("/classes/:id/invitations/:inv_id", deps.Class.DisableInvitation)
// 成员管理
auth.POST("/classes/:id/transfer", deps.Class.TransferAdmin)
auth.DELETE("/classes/:id/members/:user_id", deps.Class.RemoveMember)
// 个人照片
auth.POST("/classes/:id/photos", deps.Class.UploadPhoto)
auth.DELETE("/classes/:id/photos/:photo_id", deps.Class.DeletePhoto)
// 文件上传
auth.POST("/upload", deps.Upload.Upload)
}
// ---- 平台管理员路由 ----
admin := api.Group("/admin")
admin.Use(middleware.Auth(jm), middleware.AdminOnly())
{
admin.GET("/stats", deps.Admin.Stats)
admin.GET("/stats/detailed", deps.Admin.DetailedStats)
admin.GET("/classes", deps.Admin.AuditQueue)
admin.POST("/classes/:id/approve", deps.Admin.Approve)
admin.POST("/classes/:id/reject", deps.Admin.Reject)
admin.DELETE("/classes/:id", deps.Admin.Delete)
admin.GET("/classes/:id/logs", deps.Admin.AuditLogs)
admin.POST("/cleanup", deps.Admin.TriggerCleanup)
// 系统配置管理
admin.GET("/config", deps.Config.GetAll)
admin.PUT("/config", deps.Config.Update)
// 菜单管理
admin.GET("/menus", deps.Menu.GetAll)
admin.POST("/menus", deps.Menu.Create)
admin.PUT("/menus/:id", deps.Menu.Update)
admin.DELETE("/menus/:id", deps.Menu.Delete)
// 账号封禁管理
admin.GET("/users/banned", deps.Admin.BannedUsers)
admin.POST("/users/:id/ban", deps.Admin.BanUser)
admin.POST("/users/:id/unban", deps.Admin.UnbanUser)
// 敏感词管理
admin.GET("/sensitive-words", deps.Admin.SensitiveWords)
admin.POST("/sensitive-words", deps.Admin.AddSensitiveWord)
admin.DELETE("/sensitive-words/:id", deps.Admin.DeleteSensitiveWord)
admin.PUT("/sensitive-words/:id/toggle", deps.Admin.ToggleSensitiveWord)
}
}
// corsMiddleware 简单 CORS 处理(开发环境允许所有来源)
func corsMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
c.Header("Access-Control-Allow-Origin", "*")
c.Header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
c.Header("Access-Control-Allow-Headers", "Origin, Content-Type, Authorization, X-Requested-With")
c.Header("Access-Control-Max-Age", "86400")
if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(204)
return
}
c.Next()
}
}