|
@@ -74,6 +74,13 @@ type ActivitySeed struct {
|
|
|
// **没有这一行就进不去循环** ⇒ 两侧都 no-op ⇒ case 退化成空过。
|
|
// **没有这一行就进不去循环** ⇒ 两侧都 no-op ⇒ case 退化成空过。
|
|
|
Discounts []DiscountSeed
|
|
Discounts []DiscountSeed
|
|
|
|
|
|
|
|
|
|
+ // DiscountLogs 是要种的额度流水(负值 = 消费方向)。
|
|
|
|
|
+ //
|
|
|
|
|
+ // `user_discount_refund` 的游标是
|
|
|
|
|
+ // `WHERE courses_studentid=? AND discount_amount_used<0 ORDER BY id DESC`,
|
|
|
|
|
+ // **没有负值行就进不去循环** ⇒ 两侧都 no-op ⇒ 空过。
|
|
|
|
|
+ DiscountLogs []DiscountLogSeed
|
|
|
|
|
+
|
|
|
// Items 非 nil 时**整体替换** `C1ActivityPlan` 的默认矩阵(case 自定义构型)。
|
|
// Items 非 nil 时**整体替换** `C1ActivityPlan` 的默认矩阵(case 自定义构型)。
|
|
|
//
|
|
//
|
|
|
// `ID` 留 0 即可:由 `buildPlan` 按 `base+i` 分配。**case 里不许写死 id** ——
|
|
// `ID` 留 0 即可:由 `buildPlan` 按 `base+i` 分配。**case 里不许写死 id** ——
|
|
@@ -146,6 +153,17 @@ type DiscountSeed struct {
|
|
|
IncomeCents int64
|
|
IncomeCents int64
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// DiscountLogSeed 是一条要种的额度流水。
|
|
|
|
|
+//
|
|
|
|
|
+// `OrderNo` 留空时由种子按各侧口径填(legacy 旧 UUID / target 数字 id),与 `UsedLogSeed` 同理:
|
|
|
|
|
+// 两侧订单 id 空间不同(见 §19),填成同一个值会让一侧按自己口径查不到行。
|
|
|
|
|
+type DiscountLogSeed struct {
|
|
|
|
|
+ DiscountIndex int // plan 里 Discounts 的下标
|
|
|
|
|
+ OrderNo string
|
|
|
|
|
+ MoneyCents int64 // 负数 = 消费;正数 = 退回
|
|
|
|
|
+ Memo string
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
// buildPlan 按 case 的声明构造实际 plan(id 由 base 决定)。
|
|
// buildPlan 按 case 的声明构造实际 plan(id 由 base 决定)。
|
|
|
//
|
|
//
|
|
|
// 两条路径:
|
|
// 两条路径:
|
|
@@ -336,6 +354,23 @@ VALUES (?, ?, ?, ?, ?, ?, ?, NOW())`,
|
|
|
return fmt.Errorf("种子 legacy 赠送额度失败:%w", err)
|
|
return fmt.Errorf("种子 legacy 赠送额度失败:%w", err)
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+ // 赠送额度流水(`user_discount_refund` 的游标依据)。
|
|
|
|
|
+ for _, dl := range c.Activity.DiscountLogs {
|
|
|
|
|
+ aid := p.Items[c.Activity.Discounts[dl.DiscountIndex].ActivityIndex].ID
|
|
|
|
|
+ var poolID int64
|
|
|
|
|
+ if err := tx.Raw(`SELECT id FROM t_courses_activity_user_discount
|
|
|
|
|
+ WHERE activityid=? AND userid=? ORDER BY id DESC LIMIT 1`, aid, uid).
|
|
|
|
|
+ Row().Scan(&poolID); err != nil {
|
|
|
|
|
+ return fmt.Errorf("读回 legacy 额度池 id 失败:%w", err)
|
|
|
|
|
+ }
|
|
|
|
|
+ if err := tx.Exec(`
|
|
|
|
|
+INSERT INTO t_courses_activity_user_discount_log
|
|
|
|
|
+ (activity_user_discountid, courses_studentid, discount_amount_used, create_time, memo)
|
|
|
|
|
+VALUES (?, ?, ?, NOW(), ?)`,
|
|
|
|
|
+ poolID, firstOr(dl.OrderNo, c.StuCourseID), yuan(dl.MoneyCents), dl.Memo).Error; err != nil {
|
|
|
|
|
+ return fmt.Errorf("种子 legacy 额度流水失败:%w", err)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
// 用量流水(W2 冲回路径的定位依据)。两侧 `orderid` / `order_no` 写同一个字符串。
|
|
// 用量流水(W2 冲回路径的定位依据)。两侧 `orderid` / `order_no` 写同一个字符串。
|
|
|
for _, ul := range c.Activity.UsedLogs {
|
|
for _, ul := range c.Activity.UsedLogs {
|
|
|
aid := p.Items[ul.ActivityIndex].ID
|
|
aid := p.Items[ul.ActivityIndex].ID
|
|
@@ -405,6 +440,24 @@ VALUES (?, ?, ?, ?, ?, ?, ?, NOW(3))`,
|
|
|
return fmt.Errorf("种子 target 赠送额度失败:%w", err)
|
|
return fmt.Errorf("种子 target 赠送额度失败:%w", err)
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+ // 赠送额度流水。target 的游标按 `courses_student_id`(数字)过滤 ⇒ 必须写数字 id;
|
|
|
|
|
+ // `courses_student_no` 留 NULL(该列「禁止新写入」,见 §19)。
|
|
|
|
|
+ for _, dl := range c.Activity.DiscountLogs {
|
|
|
|
|
+ aid := p.Items[c.Activity.Discounts[dl.DiscountIndex].ActivityIndex].ID
|
|
|
|
|
+ var poolID int64
|
|
|
|
|
+ if err := tx.Raw(`SELECT id FROM t_courses_activity_user_discount
|
|
|
|
|
+ WHERE activity_id=? AND user_id=? ORDER BY id DESC LIMIT 1`, aid, uid).
|
|
|
|
|
+ Row().Scan(&poolID); err != nil {
|
|
|
|
|
+ return fmt.Errorf("读回 target 额度池 id 失败:%w", err)
|
|
|
|
|
+ }
|
|
|
|
|
+ if err := tx.Exec(`
|
|
|
|
|
+INSERT INTO t_courses_activity_user_discount_log
|
|
|
|
|
+ (activity_user_discount_id, courses_student_id, discount_amount_used, create_time, memo)
|
|
|
|
|
+VALUES (?, ?, ?, NOW(3), ?)`,
|
|
|
|
|
+ poolID, c.StuCourseNumericID, yuan(dl.MoneyCents), dl.Memo).Error; err != nil {
|
|
|
|
|
+ return fmt.Errorf("种子 target 额度流水失败:%w", err)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
// 用量流水:`order_no` 与 legacy 的 `orderid` 同一个字符串(两侧游标靠它定位)。
|
|
// 用量流水:`order_no` 与 legacy 的 `orderid` 同一个字符串(两侧游标靠它定位)。
|
|
|
for _, ul := range c.Activity.UsedLogs {
|
|
for _, ul := range c.Activity.UsedLogs {
|
|
|
aid := p.Items[ul.ActivityIndex].ID
|
|
aid := p.Items[ul.ActivityIndex].ID
|