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
+159
View File
@@ -0,0 +1,159 @@
package handler
import (
"photowall/internal/middleware"
"photowall/internal/model"
"photowall/internal/service"
"photowall/pkg/response"
"strconv"
"github.com/gin-gonic/gin"
)
type UserHandler struct {
svc *service.UserService
}
func NewUserHandler(svc *service.UserService) *UserHandler {
return &UserHandler{svc: svc}
}
// Profile GET /api/user/profile
func (h *UserHandler) Profile(c *gin.Context) {
userID := middleware.CurrentUserID(c)
profile, err := h.svc.GetProfile(userID)
if err != nil {
response.NotFound(c, err.Error())
return
}
response.OK(c, profile)
}
// UpdateProfile PUT /api/user/profile
func (h *UserHandler) UpdateProfile(c *gin.Context) {
userID := middleware.CurrentUserID(c)
var req service.UpdateProfileReq
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "参数错误")
return
}
if err := h.svc.UpdateProfile(userID, &req); err != nil {
response.Internal(c, err.Error())
return
}
response.OK(c, gin.H{"message": "更新成功"})
}
// AddContact POST /api/user/contacts
func (h *UserHandler) AddContact(c *gin.Context) {
userID := middleware.CurrentUserID(c)
var body struct {
Type model.ContactType `json:"type" binding:"required"`
Value string `json:"value" binding:"required"`
IsPrimary bool `json:"is_primary"`
}
if err := c.ShouldBindJSON(&body); err != nil {
response.BadRequest(c, "参数错误")
return
}
contact, err := h.svc.AddContact(userID, body.Type, body.Value, body.IsPrimary)
if err != nil {
response.BadRequest(c, err.Error())
return
}
response.Created(c, contact)
}
// UpdateContact PUT /api/user/contacts/:id
func (h *UserHandler) UpdateContact(c *gin.Context) {
userID := middleware.CurrentUserID(c)
id, _ := strconv.ParseUint(c.Param("id"), 10, 64)
var body struct {
Value string `json:"value"`
IsPrimary *bool `json:"is_primary"`
}
if err := c.ShouldBindJSON(&body); err != nil {
response.BadRequest(c, "参数错误")
return
}
if err := h.svc.UpdateContact(userID, uint(id), body.Value, body.IsPrimary); err != nil {
response.BadRequest(c, err.Error())
return
}
response.OK(c, gin.H{"message": "更新成功"})
}
// DeleteContact DELETE /api/user/contacts/:id
func (h *UserHandler) DeleteContact(c *gin.Context) {
userID := middleware.CurrentUserID(c)
id, _ := strconv.ParseUint(c.Param("id"), 10, 64)
if err := h.svc.DeleteContact(userID, uint(id)); err != nil {
response.BadRequest(c, err.Error())
return
}
response.OK(c, gin.H{"message": "删除成功"})
}
// AddEducation POST /api/user/education
func (h *UserHandler) AddEducation(c *gin.Context) {
userID := middleware.CurrentUserID(c)
var e model.EducationHistory
if err := c.ShouldBindJSON(&e); err != nil {
response.BadRequest(c, "参数错误")
return
}
if e.SchoolName == "" {
response.BadRequest(c, "学校名称不能为空")
return
}
if err := h.svc.AddEducation(userID, &e); err != nil {
response.Internal(c, err.Error())
return
}
response.Created(c, e)
}
// UpdateEducation PUT /api/user/education/:id
func (h *UserHandler) UpdateEducation(c *gin.Context) {
userID := middleware.CurrentUserID(c)
id, _ := strconv.ParseUint(c.Param("id"), 10, 64)
var e model.EducationHistory
if err := c.ShouldBindJSON(&e); err != nil {
response.BadRequest(c, "参数错误")
return
}
if err := h.svc.UpdateEducation(userID, uint(id), &e); err != nil {
response.BadRequest(c, err.Error())
return
}
response.OK(c, gin.H{"message": "更新成功"})
}
// DeleteEducation DELETE /api/user/education/:id
func (h *UserHandler) DeleteEducation(c *gin.Context) {
userID := middleware.CurrentUserID(c)
id, _ := strconv.ParseUint(c.Param("id"), 10, 64)
if err := h.svc.DeleteEducation(userID, uint(id)); err != nil {
response.BadRequest(c, err.Error())
return
}
response.OK(c, gin.H{"message": "删除成功"})
}
// HasEducation GET /api/user/has-education (创建班级前置检查)
func (h *UserHandler) HasEducation(c *gin.Context) {
userID := middleware.CurrentUserID(c)
response.OK(c, gin.H{"has_education": h.svc.HasEducation(userID)})
}
// PublicProfile GET /api/user/:id 公开信息(班级成员查看)
func (h *UserHandler) PublicProfile(c *gin.Context) {
id, _ := strconv.ParseUint(c.Param("id"), 10, 64)
user, err := h.svc.GetUserByID(uint(id))
if err != nil {
response.NotFound(c, err.Error())
return
}
contacts := h.svc.GetUserContacts(uint(id))
response.OK(c, gin.H{"user": user, "contacts": contacts})
}