feat(bazi): 八字页重构——空态示例排盘+字段术语表+文化声明,生辰档案绑定
Publish Mini Program Dev Version / publish (push) Failing after 11s
Build and Publish Server / build (push) Successful in 2m55s

- 八字页:未绑定生辰时展示虚构示例排盘(1990-08-08 10:30男)与15条字段说明,
  附传统文化声明文案;已绑定时支持多档案切换,排盘增强(十神/藏干/纳音/五行配色/命盘概览)
- 个人中心:新增生辰管理卡片,每用户免费绑定3个出生年月;登录改为真实微信登录
- 服务端:新增 BirthProfile 模型与 /api/user/birth-profiles 增删查接口,AutoMigrate 建表
- 前端:新增 birth-profile.js 本地存储优先+登录后云端双向同步
- 修复:个人中心 tabBar 页面入口误用 navigateTo 改为 switchTab
This commit is contained in:
gouki
2026-08-09 23:21:58 +00:00
parent 013e7ab99a
commit 50975e0627
12 changed files with 1068 additions and 196 deletions
+4
View File
@@ -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)
}
// 支付相关
+1
View File
@@ -41,6 +41,7 @@ func autoMigrate() error {
if err := DB.AutoMigrate(
&model.User{},
&model.UserProfile{},
&model.BirthProfile{},
&model.Order{},
&model.OrderItem{},
&model.WishTree{},
+106
View File
@@ -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 微信授权(仅返回 openidsession_key 属敏感凭证不下发)
func WechatAuth(c *gin.Context) {
var req struct {
+15
View File
@@ -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"`
}
+30
View File
@@ -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{