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:
@@ -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": "缺少预约单 ID(productId)"})
|
||||
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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user