feat(bazi): 八字页重构——空态示例排盘+字段术语表+文化声明,生辰档案绑定
- 八字页:未绑定生辰时展示虚构示例排盘(1990-08-08 10:30男)与15条字段说明, 附传统文化声明文案;已绑定时支持多档案切换,排盘增强(十神/藏干/纳音/五行配色/命盘概览) - 个人中心:新增生辰管理卡片,每用户免费绑定3个出生年月;登录改为真实微信登录 - 服务端:新增 BirthProfile 模型与 /api/user/birth-profiles 增删查接口,AutoMigrate 建表 - 前端:新增 birth-profile.js 本地存储优先+登录后云端双向同步 - 修复:个人中心 tabBar 页面入口误用 navigateTo 改为 switchTab
This commit is contained in:
@@ -84,6 +84,10 @@ func main() {
|
||||
user.GET("/profile", middleware.Auth(), handler.GetUserProfile)
|
||||
user.PUT("/profile", middleware.Auth(), handler.UpdateUserProfile)
|
||||
user.POST("/auth", handler.WechatAuth)
|
||||
// 生辰档案(八字绑定,每用户免费 3 个)
|
||||
user.GET("/birth-profiles", middleware.Auth(), handler.GetBirthProfiles)
|
||||
user.POST("/birth-profiles", middleware.Auth(), handler.CreateBirthProfile)
|
||||
user.DELETE("/birth-profiles/:id", middleware.Auth(), handler.DeleteBirthProfile)
|
||||
}
|
||||
|
||||
// 支付相关
|
||||
|
||||
@@ -41,6 +41,7 @@ func autoMigrate() error {
|
||||
if err := DB.AutoMigrate(
|
||||
&model.User{},
|
||||
&model.UserProfile{},
|
||||
&model.BirthProfile{},
|
||||
&model.Order{},
|
||||
&model.OrderItem{},
|
||||
&model.WishTree{},
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -283,6 +284,111 @@ func UpdateUserProfile(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// GetBirthProfiles 获取当前用户的生辰档案列表
|
||||
func GetBirthProfiles(c *gin.Context) {
|
||||
userID, exists := c.Get("userID")
|
||||
if !exists {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"code": 401, "msg": "未授权"})
|
||||
return
|
||||
}
|
||||
|
||||
userService := service.NewUserService()
|
||||
profiles, err := userService.ListBirthProfiles(userID.(uint))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"code": 500, "msg": "获取生辰档案失败"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"msg": "success",
|
||||
"data": gin.H{
|
||||
"profiles": profiles,
|
||||
"maxFree": model.MaxFreeBirthProfiles,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// CreateBirthProfile 新增生辰档案(每用户免费上限 MaxFreeBirthProfiles 个)
|
||||
func CreateBirthProfile(c *gin.Context) {
|
||||
userID, exists := c.Get("userID")
|
||||
if !exists {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"code": 401, "msg": "未授权"})
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
Name string `json:"name"`
|
||||
Gender int `json:"gender"`
|
||||
Birthday string `json:"birthday" binding:"required"`
|
||||
BirthTime string `json:"birthTime" binding:"required"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"code": 400, "msg": "参数错误"})
|
||||
return
|
||||
}
|
||||
|
||||
// 简单格式校验
|
||||
if _, err := time.Parse("2006-01-02", req.Birthday); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"code": 400, "msg": "出生日期格式错误"})
|
||||
return
|
||||
}
|
||||
if _, err := time.Parse("15:04", req.BirthTime); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"code": 400, "msg": "出生时间格式错误"})
|
||||
return
|
||||
}
|
||||
if req.Gender != 1 && req.Gender != 2 {
|
||||
req.Gender = 1
|
||||
}
|
||||
|
||||
profile := &model.BirthProfile{
|
||||
UserID: userID.(uint),
|
||||
Name: req.Name,
|
||||
Gender: req.Gender,
|
||||
Birthday: req.Birthday,
|
||||
BirthTime: req.BirthTime,
|
||||
}
|
||||
|
||||
userService := service.NewUserService()
|
||||
if err := userService.CreateBirthProfile(profile); err != nil {
|
||||
if err == service.ErrBirthProfileLimit {
|
||||
c.JSON(http.StatusForbidden, gin.H{"code": 403, "msg": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"code": 500, "msg": "新增生辰档案失败"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"msg": "success",
|
||||
"data": gin.H{"profile": profile},
|
||||
})
|
||||
}
|
||||
|
||||
// DeleteBirthProfile 删除生辰档案(仅限本人)
|
||||
func DeleteBirthProfile(c *gin.Context) {
|
||||
userID, exists := c.Get("userID")
|
||||
if !exists {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"code": 401, "msg": "未授权"})
|
||||
return
|
||||
}
|
||||
|
||||
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"code": 400, "msg": "参数错误"})
|
||||
return
|
||||
}
|
||||
|
||||
userService := service.NewUserService()
|
||||
if err := userService.DeleteBirthProfile(uint(id), userID.(uint)); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"code": 500, "msg": "删除生辰档案失败"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"code": 0, "msg": "success"})
|
||||
}
|
||||
|
||||
// WechatAuth 微信授权(仅返回 openid,session_key 属敏感凭证不下发)
|
||||
func WechatAuth(c *gin.Context) {
|
||||
var req struct {
|
||||
|
||||
@@ -32,3 +32,18 @@ type UserProfile struct {
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
|
||||
// MaxFreeBirthProfiles 每个用户可免费绑定的生辰数量上限
|
||||
const MaxFreeBirthProfiles = 3
|
||||
|
||||
// BirthProfile 用户生辰档案(八字绑定),每个用户最多免费绑定 MaxFreeBirthProfiles 个
|
||||
type BirthProfile struct {
|
||||
ID uint `gorm:"primaryKey" json:"id"`
|
||||
UserID uint `gorm:"index" json:"userId"`
|
||||
Name string `gorm:"size:32" json:"name"` // 备注名(如本人、家人等)
|
||||
Gender int `gorm:"default:1" json:"gender"` // 1:男 2:女
|
||||
Birthday string `gorm:"size:10" json:"birthday"` // YYYY-MM-DD
|
||||
BirthTime string `gorm:"size:8" json:"birthTime"` // HH:mm
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package service
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
@@ -70,6 +71,35 @@ func (s *UserService) GetUserProfile(userID uint) (*model.UserProfile, error) {
|
||||
return &profile, nil
|
||||
}
|
||||
|
||||
// ErrBirthProfileLimit 生辰档案数量达到免费上限
|
||||
var ErrBirthProfileLimit = fmt.Errorf("最多可免费绑定 %d 个生辰", model.MaxFreeBirthProfiles)
|
||||
|
||||
// ListBirthProfiles 获取用户的全部生辰档案
|
||||
func (s *UserService) ListBirthProfiles(userID uint) ([]model.BirthProfile, error) {
|
||||
var profiles []model.BirthProfile
|
||||
if err := s.db.Where("user_id = ?", userID).Order("id ASC").Find(&profiles).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return profiles, nil
|
||||
}
|
||||
|
||||
// CreateBirthProfile 新增生辰档案,超过免费上限时返回 ErrBirthProfileLimit
|
||||
func (s *UserService) CreateBirthProfile(profile *model.BirthProfile) error {
|
||||
var count int64
|
||||
if err := s.db.Model(&model.BirthProfile{}).Where("user_id = ?", profile.UserID).Count(&count).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if count >= model.MaxFreeBirthProfiles {
|
||||
return ErrBirthProfileLimit
|
||||
}
|
||||
return s.db.Create(profile).Error
|
||||
}
|
||||
|
||||
// DeleteBirthProfile 删除生辰档案(仅限本人)
|
||||
func (s *UserService) DeleteBirthProfile(id, userID uint) error {
|
||||
return s.db.Where("id = ? AND user_id = ?", id, userID).Delete(&model.BirthProfile{}).Error
|
||||
}
|
||||
|
||||
// GenerateToken 生成JWT Token
|
||||
func (s *UserService) GenerateToken(userID uint, secret string) (string, error) {
|
||||
claims := jwt.MapClaims{
|
||||
|
||||
Reference in New Issue
Block a user