更新个人资料页和文章类型需求分析,增加增值版与普通版的计价逻辑及相关字段展示。优化小程序页面,确保用户在选择导师顾问时的跳转逻辑一致性,提升用户体验。调整文档以反映最新开发进展,增强功能可用性与团队协作效率。
This commit is contained in:
@@ -64,6 +64,7 @@ var defaultCORSOrigins = []string{
|
||||
"http://127.0.0.1:5174",
|
||||
"https://soul.quwanzhi.com",
|
||||
"http://soul.quwanzhi.com",
|
||||
"https://souladmin.quwanzhi.com",
|
||||
"http://souladmin.quwanzhi.com",
|
||||
}
|
||||
|
||||
@@ -95,12 +96,49 @@ func parseCORSOrigins() []string {
|
||||
return origins
|
||||
}
|
||||
|
||||
// Load 加载配置,端口等从 .env 读取。优先从可执行文件同目录加载 .env,再试当前目录
|
||||
// Load 加载配置,端口等从 .env 读取。
|
||||
// 环境区分:APP_ENV=development 加载 .env.development,APP_ENV=production 加载 .env.production;
|
||||
// air 运行时通过 env_files 或 full_bin 设置 APP_ENV,开发用 .env.development,部署用 .env.production。
|
||||
func Load() (*Config, error) {
|
||||
workDir, _ := os.Getwd()
|
||||
execDir := ""
|
||||
if execPath, err := os.Executable(); err == nil {
|
||||
_ = godotenv.Load(filepath.Join(filepath.Dir(execPath), ".env"))
|
||||
execDir = filepath.Dir(execPath)
|
||||
}
|
||||
loadEnv := func(name string) {
|
||||
for _, dir := range []string{execDir, workDir, "."} {
|
||||
if dir == "" {
|
||||
continue
|
||||
}
|
||||
p := filepath.Join(dir, name)
|
||||
if _, err := os.Stat(p); err == nil {
|
||||
_ = godotenv.Load(p)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
overloadEnv := func(name string) {
|
||||
for _, dir := range []string{execDir, workDir, "."} {
|
||||
if dir == "" {
|
||||
continue
|
||||
}
|
||||
p := filepath.Join(dir, name)
|
||||
if _, err := os.Stat(p); err == nil {
|
||||
_ = godotenv.Overload(p)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 1. 加载 .env 作为基础
|
||||
loadEnv(".env")
|
||||
// 2. 按 APP_ENV 覆盖(优先读已设置的 APP_ENV,如 air 的 env_files 已注入)
|
||||
appEnv := strings.ToLower(strings.TrimSpace(os.Getenv("APP_ENV")))
|
||||
if appEnv == "development" {
|
||||
overloadEnv(".env.development")
|
||||
} else if appEnv == "production" {
|
||||
overloadEnv(".env.production")
|
||||
}
|
||||
_ = godotenv.Load(".env")
|
||||
|
||||
port := os.Getenv("PORT")
|
||||
if port == "" {
|
||||
|
||||
@@ -17,11 +17,13 @@ func AdminChaptersList(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
type section struct {
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Price float64 `json:"price"`
|
||||
IsFree bool `json:"isFree"`
|
||||
Status string `json:"status"`
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Price float64 `json:"price"`
|
||||
IsFree bool `json:"isFree"`
|
||||
Status string `json:"status"`
|
||||
EditionStandard *bool `json:"editionStandard,omitempty"`
|
||||
EditionPremium *bool `json:"editionPremium,omitempty"`
|
||||
}
|
||||
type chapter struct {
|
||||
ID string `json:"id"`
|
||||
@@ -60,7 +62,10 @@ func AdminChaptersList(c *gin.Context) {
|
||||
if row.Status != nil {
|
||||
st = *row.Status
|
||||
}
|
||||
ch.Sections = append(ch.Sections, section{ID: row.ID, Title: row.SectionTitle, Price: price, IsFree: isFree, Status: st})
|
||||
ch.Sections = append(ch.Sections, section{
|
||||
ID: row.ID, Title: row.SectionTitle, Price: price, IsFree: isFree, Status: st,
|
||||
EditionStandard: row.EditionStandard, EditionPremium: row.EditionPremium,
|
||||
})
|
||||
}
|
||||
structure := make([]part, 0, len(partMap))
|
||||
for _, p := range partMap {
|
||||
@@ -77,11 +82,13 @@ func AdminChaptersList(c *gin.Context) {
|
||||
// AdminChaptersAction POST/PUT/DELETE /api/admin/chapters
|
||||
func AdminChaptersAction(c *gin.Context) {
|
||||
var body struct {
|
||||
Action string `json:"action"`
|
||||
ID string `json:"id"`
|
||||
Price *float64 `json:"price"`
|
||||
IsFree *bool `json:"isFree"`
|
||||
Status *string `json:"status"`
|
||||
Action string `json:"action"`
|
||||
ID string `json:"id"`
|
||||
Price *float64 `json:"price"`
|
||||
IsFree *bool `json:"isFree"`
|
||||
Status *string `json:"status"`
|
||||
EditionStandard *bool `json:"editionStandard"`
|
||||
EditionPremium *bool `json:"editionPremium"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&body); err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "请求体无效"})
|
||||
@@ -97,5 +104,17 @@ func AdminChaptersAction(c *gin.Context) {
|
||||
if body.Action == "updateStatus" && body.ID != "" && body.Status != nil {
|
||||
db.Model(&model.Chapter{}).Where("id = ?", body.ID).Update("status", *body.Status)
|
||||
}
|
||||
if body.Action == "updateEdition" && body.ID != "" {
|
||||
updates := make(map[string]interface{})
|
||||
if body.EditionStandard != nil {
|
||||
updates["edition_standard"] = *body.EditionStandard
|
||||
}
|
||||
if body.EditionPremium != nil {
|
||||
updates["edition_premium"] = *body.EditionPremium
|
||||
}
|
||||
if len(updates) > 0 {
|
||||
db.Model(&model.Chapter{}).Where("id = ?", body.ID).Updates(updates)
|
||||
}
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"success": true})
|
||||
}
|
||||
|
||||
@@ -151,11 +151,18 @@ func BookChapters(c *gin.Context) {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"success": false, "error": "缺少 id"})
|
||||
return
|
||||
}
|
||||
if err := db.Model(&model.Chapter{}).Where("id = ?", body.ID).Updates(map[string]interface{}{
|
||||
updates := map[string]interface{}{
|
||||
"part_title": body.PartTitle, "chapter_title": body.ChapterTitle, "section_title": body.SectionTitle,
|
||||
"content": body.Content, "word_count": body.WordCount, "is_free": body.IsFree, "price": body.Price,
|
||||
"sort_order": body.SortOrder, "status": body.Status,
|
||||
}).Error; err != nil {
|
||||
}
|
||||
if body.EditionStandard != nil {
|
||||
updates["edition_standard"] = body.EditionStandard
|
||||
}
|
||||
if body.EditionPremium != nil {
|
||||
updates["edition_premium"] = body.EditionPremium
|
||||
}
|
||||
if err := db.Model(&model.Chapter{}).Where("id = ?", body.ID).Updates(updates).Error; err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -179,12 +179,14 @@ func DBBookAction(c *gin.Context) {
|
||||
}
|
||||
case http.MethodPut:
|
||||
var body struct {
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Content string `json:"content"`
|
||||
Price *float64 `json:"price"`
|
||||
IsFree *bool `json:"isFree"`
|
||||
IsNew *bool `json:"isNew"` // stitch_soul:标记最新新增
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Content string `json:"content"`
|
||||
Price *float64 `json:"price"`
|
||||
IsFree *bool `json:"isFree"`
|
||||
IsNew *bool `json:"isNew"` // stitch_soul:标记最新新增
|
||||
EditionStandard *bool `json:"editionStandard"` // 是否属于普通版
|
||||
EditionPremium *bool `json:"editionPremium"` // 是否属于增值版
|
||||
}
|
||||
if err := c.ShouldBindJSON(&body); err != nil || body.ID == "" {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "缺少 id 或请求体无效"})
|
||||
@@ -209,6 +211,12 @@ func DBBookAction(c *gin.Context) {
|
||||
if body.IsNew != nil {
|
||||
updates["is_new"] = *body.IsNew
|
||||
}
|
||||
if body.EditionStandard != nil {
|
||||
updates["edition_standard"] = *body.EditionStandard
|
||||
}
|
||||
if body.EditionPremium != nil {
|
||||
updates["edition_premium"] = *body.EditionPremium
|
||||
}
|
||||
err := db.Model(&model.Chapter{}).Where("id = ?", body.ID).Updates(updates).Error
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": err.Error()})
|
||||
|
||||
@@ -757,17 +757,31 @@ func MiniprogramUsers(c *gin.Context) {
|
||||
var cnt int64
|
||||
db.Model(&model.Order{}).Where("user_id = ? AND status = ? AND (product_type = ? OR product_type = ?)",
|
||||
id, "paid", "fullbook", "vip").Count(&cnt)
|
||||
// 用户信息(nickname/avatar)与会员资料(vip*)区分,字段与 model 一致(camelCase)
|
||||
// 用户信息与会员资料(vip*)、P3 资料扩展,供会员详情页完整展示
|
||||
item := gin.H{
|
||||
"id": user.ID,
|
||||
"nickname": getStringValue(user.Nickname),
|
||||
"avatar": getStringValue(user.Avatar),
|
||||
"vipName": getStringValue(user.VipName),
|
||||
"vipAvatar": getStringValue(user.VipAvatar),
|
||||
"vipContact": getStringValue(user.VipContact),
|
||||
"vipProject": getStringValue(user.VipProject),
|
||||
"vipBio": getStringValue(user.VipBio),
|
||||
"is_vip": cnt > 0,
|
||||
"id": user.ID,
|
||||
"nickname": getStringValue(user.Nickname),
|
||||
"avatar": getStringValue(user.Avatar),
|
||||
"phone": getStringValue(user.Phone),
|
||||
"wechatId": getStringValue(user.WechatID),
|
||||
"vipName": getStringValue(user.VipName),
|
||||
"vipAvatar": getStringValue(user.VipAvatar),
|
||||
"vipContact": getStringValue(user.VipContact),
|
||||
"vipProject": getStringValue(user.VipProject),
|
||||
"vipBio": getStringValue(user.VipBio),
|
||||
"mbti": getStringValue(user.Mbti),
|
||||
"region": getStringValue(user.Region),
|
||||
"industry": getStringValue(user.Industry),
|
||||
"position": getStringValue(user.Position),
|
||||
"businessScale": getStringValue(user.BusinessScale),
|
||||
"skills": getStringValue(user.Skills),
|
||||
"storyBestMonth": getStringValue(user.StoryBestMonth),
|
||||
"storyAchievement": getStringValue(user.StoryAchievement),
|
||||
"storyTurning": getStringValue(user.StoryTurning),
|
||||
"helpOffer": getStringValue(user.HelpOffer),
|
||||
"helpNeed": getStringValue(user.HelpNeed),
|
||||
"projectIntro": getStringValue(user.ProjectIntro),
|
||||
"is_vip": cnt > 0,
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "data": item})
|
||||
return
|
||||
|
||||
@@ -18,8 +18,11 @@ type Chapter struct {
|
||||
SortOrder *int `gorm:"column:sort_order" json:"sortOrder,omitempty"`
|
||||
Status *string `gorm:"column:status;size:20" json:"status,omitempty"`
|
||||
IsNew *bool `gorm:"column:is_new" json:"isNew,omitempty"` // stitch_soul:目录/首页「最新新增」标记
|
||||
CreatedAt time.Time `gorm:"column:created_at" json:"createdAt"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at" json:"updatedAt"`
|
||||
// 普通版/增值版:两者分开互斥,添加文章时勾选归属
|
||||
EditionStandard *bool `gorm:"column:edition_standard" json:"editionStandard,omitempty"` // 是否属于普通版
|
||||
EditionPremium *bool `gorm:"column:edition_premium" json:"editionPremium,omitempty"` // 是否属于增值版
|
||||
CreatedAt time.Time `gorm:"column:created_at" json:"createdAt"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at" json:"updatedAt"`
|
||||
}
|
||||
|
||||
func (Chapter) TableName() string { return "chapters" }
|
||||
|
||||
Reference in New Issue
Block a user