chore: update experience index and documentation for various roles

- Added recent experience entries for roles including Assistant, Mini Program Developer, Backend Engineer, Product Manager, Software Tester, and Team.
- Enhanced documentation structure in the development assistant's experience list and project index.
- Updated role-flow control documentation to reflect new methodologies and programming thinking.
- Improved error handling in the mini program and backend API for better user feedback during operations.

Made-with: Cursor
This commit is contained in:
乘风
2026-04-14 11:24:18 +08:00
parent e391115938
commit 35e18a7ccb
44 changed files with 502 additions and 43 deletions

View File

@@ -432,6 +432,15 @@ func miniprogramPayPost(c *gin.Context) {
db := database.DB()
// 尽早解析 userId便于 mentor_consultation 等按用户校验预约单与标准价
userIDForPrice := strings.TrimSpace(req.UserID)
if userIDForPrice == "" && strings.TrimSpace(req.OpenID) != "" {
var u model.User
if err := db.Where("open_id = ?", strings.TrimSpace(req.OpenID)).First(&u).Error; err == nil {
userIDForPrice = u.ID
}
}
productID := strings.TrimSpace(req.ProductID)
if req.ProductType == "link_karuo_tip" && strings.TrimSpace(req.TipSource) == "live_mic" && productID != "" && !strings.HasPrefix(productID, "live_mic|") {
productID = "live_mic|" + productID
@@ -460,7 +469,7 @@ func miniprogramPayPost(c *gin.Context) {
if pricePID == "" {
pricePID = strings.TrimSpace(req.ProductID)
}
standardPrice, priceErr := getStandardPrice(db, req.ProductType, pricePID, req.TipSource)
standardPrice, priceErr := getStandardPrice(db, req.ProductType, pricePID, userIDForPrice, req.TipSource)
if priceErr != nil && req.ProductType == "link_karuo_tip" {
if fb, _, canonID, ferr := linkKaruoTipFallbackByAmount(db, pricePID, req.Amount, req.TipSource); ferr == nil {
standardPrice = fb
@@ -537,7 +546,10 @@ func miniprogramPayPost(c *gin.Context) {
}
// userID优先用客户端传入为空时按 openid 查用户(排除软删除,避免订单归属到旧账号)
userID := req.UserID
userID := strings.TrimSpace(req.UserID)
if userID == "" && userIDForPrice != "" {
userID = userIDForPrice
}
if userID == "" && req.OpenID != "" {
var u model.User
if err := db.Where("open_id = ?", req.OpenID).First(&u).Error; err == nil {
@@ -558,6 +570,9 @@ func miniprogramPayPost(c *gin.Context) {
case "link_karuo_tip":
c.JSON(http.StatusBadRequest, gin.H{"success": false, "error": "打赏缺少礼物标识"})
return
case "mentor_consultation":
c.JSON(http.StatusBadRequest, gin.H{"success": false, "error": "缺少预约单 IDproductId"})
return
default:
productID = "fullbook"
}
@@ -575,6 +590,8 @@ func miniprogramPayPost(c *gin.Context) {
description = "卡若创业派对VIP年度会员365天"
case "match":
description = "购买匹配次数"
case "mentor_consultation":
description = "导师咨询服务"
case "link_karuo_tip":
giftID := productID
if strings.HasPrefix(giftID, "live_mic|") {
@@ -912,6 +929,13 @@ func MiniprogramPayNotify(c *gin.Context) {
}
} else if attach.ProductType == "link_karuo_tip" {
fmt.Printf("[PayNotify] 打赏/上麦礼遇订单完成: user=%s order=%s\n", beneficiaryUserID, orderSn)
} else if attach.ProductType == "mentor_consultation" && attach.ProductID != "" {
if cid, err := strconv.Atoi(strings.TrimSpace(attach.ProductID)); err == nil && cid > 0 {
_ = db.Model(&model.MentorConsultation{}).
Where("id = ? AND user_id = ?", cid, beneficiaryUserID).
Updates(map[string]interface{}{"status": "paid", "updated_at": time.Now()})
fmt.Printf("[PayNotify] 导师预约已支付: user=%s consultId=%d order=%s\n", beneficiaryUserID, cid, orderSn)
}
}
productID := attach.ProductID
if productID == "" {
@@ -953,6 +977,10 @@ func processReferralCommission(db *gorm.DB, buyerUserID string, amount float64,
fmt.Printf("[PayNotify] 打赏订单跳过分销佣金: orderSn=%s\n", orderSn)
return
}
if order != nil && order.ProductType == "mentor_consultation" {
fmt.Printf("[PayNotify] 导师咨询订单跳过分销佣金: orderSn=%s\n", orderSn)
return
}
type Binding struct {
ID int `gorm:"column:id"`
ReferrerID string `gorm:"column:referrer_id"`
@@ -1326,6 +1354,17 @@ func activateOrderBenefits(db *gorm.DB, order *model.Order, payTime time.Time) {
ConfirmBalanceRechargeByOrder(db, order)
case "link_karuo_tip":
// 首页打赏 / 上麦礼遇:仅收款,无额外会员或章节权益
case "mentor_consultation":
if order.ProductID == nil || strings.TrimSpace(*order.ProductID) == "" {
return
}
cid, err := strconv.Atoi(strings.TrimSpace(*order.ProductID))
if err != nil || cid <= 0 {
return
}
_ = db.Model(&model.MentorConsultation{}).
Where("id = ? AND user_id = ?", cid, userID).
Updates(map[string]interface{}{"status": "paid", "updated_at": payTime})
}
}
@@ -1671,10 +1710,11 @@ func linkKaruoTipPriceTotal(db *gorm.DB, productID string, tipSource string) (fl
}
// getStandardPrice 从 DB 读取商品标准价(后端校验用),防止客户端篡改金额
// productType: fullbook / vip / section / match / link_karuo_tip
// productId: 章节购买时为章节 ID打赏为 giftId|qty 或 live_mic|giftId|qty
// productType: fullbook / vip / section / match / link_karuo_tip / mentor_consultation
// productId: 章节购买时为章节 ID打赏为 giftId|qty 或 live_mic|giftId|qty;导师咨询为 mentor_consultations.id
// buyerUserID: mentor_consultation 时校验预约归属;其他类型可传空字符串
// tipSource 可选live_mic / home_reward 时 link_karuo_tip 计价与置顶人物礼物配置合并
func getStandardPrice(db *gorm.DB, productType, productID string, tipSource ...string) (float64, error) {
func getStandardPrice(db *gorm.DB, productType, productID, buyerUserID string, tipSource ...string) (float64, error) {
productType = normalizePayProductType(productType)
ts := ""
if len(tipSource) > 0 {
@@ -1767,6 +1807,29 @@ func getStandardPrice(db *gorm.DB, productType, productID string, tipSource ...s
}
return *ch.Price, nil
case "mentor_consultation":
if productID == "" {
return 0, fmt.Errorf("导师咨询缺少预约单 ID")
}
cid, err := strconv.Atoi(strings.TrimSpace(productID))
if err != nil || cid <= 0 {
return 0, fmt.Errorf("无效的预约单 ID")
}
var mc model.MentorConsultation
if err := db.Where("id = ?", cid).First(&mc).Error; err != nil {
return 0, fmt.Errorf("预约单不存在")
}
if mc.Status != "" && mc.Status != "created" {
return 0, fmt.Errorf("预约单状态不可支付")
}
if strings.TrimSpace(buyerUserID) != "" && mc.UserID != buyerUserID {
return 0, fmt.Errorf("预约单与当前用户不匹配")
}
if mc.Amount <= 0 {
return 0, fmt.Errorf("预约金额无效")
}
return mc.Amount, nil
default:
return 0, fmt.Errorf("未知商品类型: %s", productType)
}