- 新增存储空间管理:会员套餐配置、用户容量/到期管理、后台支付记录查询 - 接入个人主体小程序虚拟支付:下单/发货推送/兜底查单(query_order) - 修复 adminStorageUsers 等接口鉴权、out_trade_no 长度、前端 requestVirtualPayment 参数 - 前端上传前容量预检,超容引导升级无限空间 - 我的页会员卡片展示「无限空间」与已用容量/到期时间 - 后台「存储空间管理」新增支付记录标签页
583 lines
20 KiB
Go
583 lines
20 KiB
Go
package handler
|
||
|
||
import (
|
||
"fmt"
|
||
"strings"
|
||
|
||
"past/internal/httpapi"
|
||
"past/internal/store"
|
||
)
|
||
|
||
func registerPeriod(r *httpapi.Router) {
|
||
r.Handle("period_list", periodList)
|
||
r.Handle("period_add", periodProtected(periodAdd))
|
||
r.Handle("period_update", periodProtected(periodUpdate))
|
||
r.Handle("period_delete", periodProtected(periodDelete))
|
||
r.Handle("period_visible_get", periodVisibleGet)
|
||
r.Handle("period_visible_set", periodVisibleSet)
|
||
r.Handle("period_source_select", periodSourceSelect)
|
||
r.Handle("period_daily_save", periodProtected(periodDailySave))
|
||
r.Handle("period_daily_get", periodProtected(periodDailyGet))
|
||
r.Handle("period_sex_add", periodProtected(periodSexAdd))
|
||
r.Handle("period_sex_delete", periodProtected(periodSexDelete))
|
||
r.Handle("period_sex_list", periodProtected(periodSexList))
|
||
}
|
||
|
||
func periodProtected(next httpapi.HandlerFunc) httpapi.HandlerFunc {
|
||
return func(c *httpapi.Ctx) error {
|
||
if err := requirePeriodAccess(c); err != nil {
|
||
return err
|
||
}
|
||
if required, _, _, err := periodSourceState(c); err != nil {
|
||
return err
|
||
} else if required {
|
||
return c.Err(409, "请先选择姨姨记录来源")
|
||
}
|
||
return next(c)
|
||
}
|
||
}
|
||
|
||
// cidOrNil 对应 PHP 中直接把 getCoupleId()(可能为 null)绑定进 SQL 的场景:未绑定时传 NULL 而非 0
|
||
func cidOrNil(c *httpapi.Ctx) any {
|
||
if cid := c.CoupleID(); cid != 0 {
|
||
return cid
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// coupleCond 根据 cid(可能为 nil)构造 couple_id 过滤条件与参数
|
||
func coupleCond(cid any) (string, []any) {
|
||
if cid == nil {
|
||
return "couple_id IS NULL", nil
|
||
}
|
||
return "couple_id = ?", []any{cid}
|
||
}
|
||
|
||
// 每人独立控制自己是否向伴侣开放同一份姨姨数据。
|
||
func periodVisible(c *httpapi.Ctx, uid int64) int64 {
|
||
row, _ := c.DB.QueryOne("SELECT period_visible FROM users WHERE id = ?", uid)
|
||
if row == nil {
|
||
return 0
|
||
}
|
||
return row.Int("period_visible")
|
||
}
|
||
|
||
func emptyPeriod(visible int64) M {
|
||
return M{"cycles": []any{}, "daily": []any{}, "sex_records": []any{}, "period_visible": visible, "can_access": false}
|
||
}
|
||
|
||
type periodAccess struct {
|
||
uid, cid, partnerID int64
|
||
visible, partnerVisible int64
|
||
}
|
||
|
||
func currentPeriodAccess(c *httpapi.Ctx) periodAccess {
|
||
uid := c.UID()
|
||
cid := c.CoupleID()
|
||
access := periodAccess{uid: uid, cid: cid, visible: periodVisible(c, uid), partnerVisible: 1}
|
||
if cid != 0 {
|
||
access.partnerVisible = 0
|
||
partner, _ := c.DB.QueryOne("SELECT id, period_visible FROM users WHERE couple_id = ? AND id != ? LIMIT 1", cid, uid)
|
||
if partner != nil {
|
||
access.partnerID = partner.Int("id")
|
||
access.partnerVisible = partner.Int("period_visible")
|
||
}
|
||
}
|
||
return access
|
||
}
|
||
|
||
func (a periodAccess) canAccess() bool {
|
||
return a.partnerVisible != 0
|
||
}
|
||
|
||
func requirePeriodAccess(c *httpapi.Ctx) error {
|
||
if !currentPeriodAccess(c).canAccess() {
|
||
return c.Err(403, "对方已关闭姨姨可见权限")
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func periodList(c *httpapi.Ctx) error {
|
||
access := currentPeriodAccess(c)
|
||
if !access.canAccess() {
|
||
return c.OK(emptyPeriod(access.visible))
|
||
}
|
||
selectionRequired, me, partner, err := periodSourceState(c)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
if selectionRequired {
|
||
myPreview, err := periodSourcePreview(c.DB, me)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
partnerPreview, err := periodSourcePreview(c.DB, partner)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return c.OK(M{"cycles": []any{}, "daily": []any{}, "sex_records": []any{},
|
||
"period_visible": access.visible, "can_access": true, "has_couple": true,
|
||
"selection_required": true, "my_user_id": me, "partner_user_id": partner,
|
||
"my_preview": myPreview, "partner_preview": partnerPreview})
|
||
}
|
||
|
||
// 姨姨在绑定后仍是一份情侣数据;未绑定时只读取自己的记录。
|
||
var where string
|
||
var args []any
|
||
if access.cid != 0 {
|
||
where = "couple_id = ?"
|
||
args = []any{access.cid}
|
||
} else {
|
||
where = "couple_id IS NULL AND user_id = ?"
|
||
args = []any{access.uid}
|
||
}
|
||
|
||
cycles, err := c.DB.Query("SELECT * FROM period_records WHERE "+where+" ORDER BY start_date DESC", args...)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
daily, err := c.DB.Query("SELECT * FROM period_daily_records WHERE "+where+" ORDER BY record_date DESC", args...)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
sex, err := c.DB.Query("SELECT * FROM period_sex_records WHERE "+where+" ORDER BY record_date DESC, record_time ASC", args...)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
cycleRows := rows(cycles)
|
||
dailyRows := rows(daily)
|
||
// 经期结束以「最后一次标记有经期(period>0)的日期」为准:若周期记录的结束日期是预估的未来日期,
|
||
// 则按实际最后有经期日期收敛,让前端能正确显示「已结束」。
|
||
for _, cy := range cycleRows {
|
||
start, end := cy.Str("start_date"), cy.Str("end_date")
|
||
last := ""
|
||
for _, d := range dailyRows {
|
||
rd := d.Str("record_date")
|
||
if rd < start || rd > end {
|
||
continue
|
||
}
|
||
switch d.Str("period") {
|
||
case "1", "2", "3":
|
||
if rd > last {
|
||
last = rd
|
||
}
|
||
}
|
||
}
|
||
if last != "" && last < end {
|
||
cy["end_date"] = last
|
||
}
|
||
}
|
||
return c.OK(M{"cycles": cycleRows, "daily": dailyRows, "sex_records": rows(sex), "period_visible": access.visible, "can_access": true, "has_couple": access.cid != 0})
|
||
}
|
||
|
||
func periodAdd(c *httpapi.Ctx) error {
|
||
if err := c.RequirePOST(); err != nil {
|
||
return err
|
||
}
|
||
cid := cidOrNil(c)
|
||
uid := c.UID()
|
||
startDate, endDate := c.InTrim("start_date"), c.InTrim("end_date")
|
||
if !store.Truthy(startDate) || !store.Truthy(endDate) {
|
||
return c.Err(400, "请选择日期")
|
||
}
|
||
if err := checkTextOr400(c, strings.TrimSpace(c.InStr("symptoms")+" "+c.InStr("note"))); err != nil {
|
||
return err
|
||
}
|
||
cycleLen, periodLen := int64(28), int64(5)
|
||
if c.InHas("cycle_length") {
|
||
cycleLen = c.InInt("cycle_length")
|
||
}
|
||
if c.InHas("period_length") {
|
||
periodLen = c.InInt("period_length")
|
||
}
|
||
recID, err := c.DB.Insert("INSERT INTO period_records (couple_id, user_id, start_date, end_date, cycle_length, period_length, symptoms, note) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
|
||
cid, uid, startDate, endDate, cycleLen, periodLen, inOr(c, "symptoms", ""), inOr(c, "note", ""))
|
||
if err != nil {
|
||
return err
|
||
}
|
||
// 区间内每天创建 period_daily_records(对应 DatePeriod,含结束日)。
|
||
// 未来日期尚未确认经期量,不预先标记为有经期,避免把预估结束日误判为实际经期。
|
||
start, err := parseDate(startDate)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
end, err := parseDate(endDate)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
for d := start; !d.After(end) && !d.After(startOfToday()); d = d.AddDate(0, 0, 1) {
|
||
if _, err := c.DB.Exec("INSERT INTO period_daily_records (couple_id, user_id, record_date, period) VALUES (?, ?, ?, '1') ON DUPLICATE KEY UPDATE period = VALUES(period)", cid, uid, d.Format("2006-01-02")); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
actID, err := logActivityErr(c, cid, uid, "period_add", "记录了经期("+startDate+" ~ "+endDate+")")
|
||
if err != nil {
|
||
return err
|
||
}
|
||
if partnerID := partnerUserId(c.DB, uid); partnerID != 0 {
|
||
if periodVisible(c, uid) != 0 {
|
||
c.App.Wechat.SendSubscribeMessage(partnerID, "notify", map[string]string{
|
||
"thing1": "经期记录有更新",
|
||
"thing2": fmt.Sprintf("TA记录了经期(%d天),记得多关心哦 🌸", periodLen),
|
||
"date3": startDate,
|
||
"thing4": "点击查看详情",
|
||
}, "pages/period/period", actID, uid)
|
||
}
|
||
}
|
||
// PHP 返回的是 logActivity 之后的 lastInsertId(activity_log 的 id);Go 返回真实记录 id
|
||
return c.OK(M{"success": true, "id": fmt.Sprint(recID)})
|
||
}
|
||
|
||
// logActivityErr 与 logActivity 相同,但把 SQL 错误返回(对应 PHP 中会抛异常导致 500 的调用点)。
|
||
// 未绑定情侣(coupleID 为 nil)时跳过日志:activity_log.couple_id 为 NOT NULL,未绑定时无从归属。
|
||
func logActivityErr(c *httpapi.Ctx, coupleID any, userID int64, actionType, description string) (int64, error) {
|
||
if coupleID == nil {
|
||
return 0, nil
|
||
}
|
||
return c.DB.Insert("INSERT INTO activity_log (couple_id, user_id, action_type, description, detail, created_at) VALUES (?, ?, ?, ?, ?, NOW())",
|
||
coupleID, userID, actionType, description, "")
|
||
}
|
||
|
||
func periodUpdate(c *httpapi.Ctx) error {
|
||
if err := c.RequirePOST(); err != nil {
|
||
return err
|
||
}
|
||
cid := cidOrNil(c)
|
||
uid := c.UID()
|
||
id := c.InInt("id")
|
||
if id == 0 {
|
||
return c.Err(400, "缺少记录ID")
|
||
}
|
||
if err := checkTextOr400(c, strings.TrimSpace(c.InStr("symptoms")+" "+c.InStr("note"))); err != nil {
|
||
return err
|
||
}
|
||
cycleLen, periodLen := int64(28), int64(5)
|
||
if c.InHas("cycle_length") {
|
||
cycleLen = c.InInt("cycle_length")
|
||
}
|
||
if c.InHas("period_length") {
|
||
periodLen = c.InInt("period_length")
|
||
}
|
||
startDate, endDate := c.InTrim("start_date"), c.InTrim("end_date")
|
||
start, err := parseDate(startDate)
|
||
if err != nil {
|
||
return c.Err(400, "开始日期无效")
|
||
}
|
||
end, err := parseDate(endDate)
|
||
if err != nil || end.Before(start) {
|
||
return c.Err(400, "结束日期无效")
|
||
}
|
||
cond, condArgs := coupleCond(cid)
|
||
if cid == nil {
|
||
cond += " AND user_id = ?"
|
||
condArgs = append(condArgs, uid)
|
||
}
|
||
tx, err := c.DB.Begin()
|
||
if err != nil {
|
||
return err
|
||
}
|
||
defer tx.Rollback()
|
||
oldArgs := append([]any{id}, condArgs...)
|
||
old, err := tx.QueryOne("SELECT start_date, end_date FROM period_records WHERE id = ? AND "+cond+" FOR UPDATE", oldArgs...)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
if old == nil {
|
||
return c.Err(404, "周期记录不存在")
|
||
}
|
||
args := []any{startDate, endDate, cycleLen, periodLen, inOr(c, "symptoms", ""), inOr(c, "note", ""), id}
|
||
args = append(args, condArgs...)
|
||
if _, err := tx.Exec("UPDATE period_records SET start_date = ?, end_date = ?, cycle_length = ?, period_length = ?, symptoms = ?, note = ? WHERE id = ? AND "+cond, args...); err != nil {
|
||
return err
|
||
}
|
||
// 同步日历标记:去掉旧区间中不再属于任何周期的日期,再标记新区间。
|
||
oldStart, err := parseDate(old.Str("start_date"))
|
||
if err != nil {
|
||
return err
|
||
}
|
||
oldEnd, err := parseDate(old.Str("end_date"))
|
||
if err != nil {
|
||
return err
|
||
}
|
||
for d := oldStart; !d.After(oldEnd); d = d.AddDate(0, 0, 1) {
|
||
if !d.Before(start) && !d.After(end) {
|
||
continue
|
||
}
|
||
date := d.Format("2006-01-02")
|
||
checkArgs := append(append([]any{}, condArgs...), date, date)
|
||
other, err := tx.QueryOne("SELECT id FROM period_records WHERE "+cond+" AND start_date <= ? AND end_date >= ? LIMIT 1", checkArgs...)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
if other == nil {
|
||
clearArgs := append(append([]any{}, condArgs...), date)
|
||
if _, err := tx.Exec("UPDATE period_daily_records SET period = '0' WHERE "+cond+" AND record_date = ?", clearArgs...); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
}
|
||
for d := start; !d.After(end) && !d.After(startOfToday()); d = d.AddDate(0, 0, 1) {
|
||
date := d.Format("2006-01-02")
|
||
if cid == nil {
|
||
// MySQL 的 UNIQUE 索引允许多个 NULL;未绑定记录必须按用户和日期查找。
|
||
existing, err := tx.QueryOne("SELECT id FROM period_daily_records WHERE couple_id IS NULL AND user_id = ? AND record_date = ? LIMIT 1", uid, date)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
if existing != nil {
|
||
if _, err := tx.Exec("UPDATE period_daily_records SET period = '1' WHERE couple_id IS NULL AND user_id = ? AND record_date = ?", uid, date); err != nil {
|
||
return err
|
||
}
|
||
continue
|
||
}
|
||
}
|
||
if _, err := tx.Exec("INSERT INTO period_daily_records (couple_id, user_id, record_date, period) VALUES (?, ?, ?, '1') ON DUPLICATE KEY UPDATE period = VALUES(period)", cid, uid, date); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
if err := tx.Commit(); err != nil {
|
||
return err
|
||
}
|
||
return c.OK(M{"success": true})
|
||
}
|
||
|
||
func periodDelete(c *httpapi.Ctx) error {
|
||
if err := c.RequirePOST(); err != nil {
|
||
return err
|
||
}
|
||
cid := cidOrNil(c)
|
||
uid := c.UID()
|
||
id := c.InInt("id")
|
||
startDate, endDate := c.InTrim("start_date"), c.InTrim("end_date")
|
||
cond, condArgs := coupleCond(cid)
|
||
if cid == nil {
|
||
cond += " AND user_id = ?"
|
||
condArgs = append(condArgs, uid)
|
||
}
|
||
if id != 0 {
|
||
delArgs := append([]any{id}, condArgs...)
|
||
if _, err := c.DB.Exec("DELETE FROM period_records WHERE id = ? AND "+cond, delArgs...); err != nil {
|
||
return err
|
||
}
|
||
} else if store.Truthy(startDate) && store.Truthy(endDate) {
|
||
delArgs := append(append([]any{}, condArgs...), startDate, endDate)
|
||
if _, err := c.DB.Exec("DELETE FROM period_records WHERE "+cond+" AND start_date = ? AND end_date = ?", delArgs...); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
if store.Truthy(startDate) && store.Truthy(endDate) {
|
||
updArgs := append(append([]any{}, condArgs...), startDate, endDate)
|
||
if _, err := c.DB.Exec("UPDATE period_daily_records SET period = '0' WHERE "+cond+" AND record_date >= ? AND record_date <= ?", updArgs...); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
if _, err := logActivityErr(c, cid, uid, "period_delete", "删除了经期周期记录"); err != nil {
|
||
return err
|
||
}
|
||
return c.OK(M{"success": true})
|
||
}
|
||
|
||
func periodVisibleGet(c *httpapi.Ctx) error {
|
||
access := currentPeriodAccess(c)
|
||
return c.OK(M{"period_visible": access.visible, "can_access": access.canAccess(), "has_couple": access.cid != 0})
|
||
}
|
||
|
||
func periodVisibleSet(c *httpapi.Ctx) error {
|
||
if err := c.RequirePOST(); err != nil {
|
||
return err
|
||
}
|
||
visible := c.InInt("period_visible")
|
||
if visible != 0 && visible != 1 {
|
||
return c.Err(400, "可见权限只能为 0 或 1")
|
||
}
|
||
if _, err := c.DB.Exec("UPDATE users SET period_visible = ? WHERE id = ?", visible, c.UID()); err != nil {
|
||
return err
|
||
}
|
||
return c.OK(M{"success": true, "period_visible": visible})
|
||
}
|
||
|
||
func periodDailySave(c *httpapi.Ctx) error {
|
||
if err := c.RequirePOST(); err != nil {
|
||
return err
|
||
}
|
||
cid := cidOrNil(c)
|
||
uid := c.UID()
|
||
recordDate := c.InTrim("record_date")
|
||
if !store.Truthy(recordDate) {
|
||
return c.Err(400, "请选择日期")
|
||
}
|
||
if err := checkTextOr400(c, strings.TrimSpace(c.InStr("symptoms")+" "+c.InStr("mood")+" "+c.InStr("discharge")+" "+c.InStr("note"))); err != nil {
|
||
return err
|
||
}
|
||
// $input['temperature'] !== '' && !== null ? floatval(...) : null
|
||
numOrNil := func(key string) any {
|
||
v := c.In(key)
|
||
if v == nil || v == "" {
|
||
return nil
|
||
}
|
||
return store.ToFloat(v)
|
||
}
|
||
values := []any{store.ToStr(inOr(c, "period", "0")), store.ToStr(inOr(c, "sex", "0")),
|
||
inOr(c, "symptoms", ""), inOr(c, "mood", ""), inOr(c, "discharge", ""),
|
||
numOrNil("temperature"), numOrNil("weight"), inOr(c, "note", ""), inOr(c, "period_detail", "")}
|
||
if cid == nil {
|
||
// UNIQUE(record_date, couple_id) 对 NULL 不生效;按本人和日期更新已有行。
|
||
existing, err := c.DB.QueryOne("SELECT id FROM period_daily_records WHERE couple_id IS NULL AND user_id = ? AND record_date = ? LIMIT 1", uid, recordDate)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
if existing != nil {
|
||
args := append(append([]any{}, values...), uid, recordDate)
|
||
if _, err := c.DB.Exec("UPDATE period_daily_records SET period = ?, sex = ?, symptoms = ?, mood = ?, discharge = ?, temperature = ?, weight = ?, note = ?, period_detail = ?, updated_at = NOW() WHERE couple_id IS NULL AND user_id = ? AND record_date = ?", args...); err != nil {
|
||
return err
|
||
}
|
||
} else {
|
||
args := append([]any{nil, uid, recordDate}, values...)
|
||
if _, err := c.DB.Exec("INSERT INTO period_daily_records (couple_id, user_id, record_date, period, sex, symptoms, mood, discharge, temperature, weight, note, period_detail) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", args...); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
} else {
|
||
args := append([]any{cid, uid, recordDate}, values...)
|
||
if _, err := c.DB.Exec("INSERT INTO period_daily_records (couple_id, user_id, record_date, period, sex, symptoms, mood, discharge, temperature, weight, note, period_detail) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE period = VALUES(period), sex = VALUES(sex), symptoms = VALUES(symptoms), mood = VALUES(mood), discharge = VALUES(discharge), temperature = VALUES(temperature), weight = VALUES(weight), note = VALUES(note), period_detail = VALUES(period_detail), updated_at = NOW()", args...); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
// 若本次将月经标记设为无,检查所属周期是否还有月经记录
|
||
np := inOr(c, "period", "0")
|
||
if np == "0" || np == int64(0) || np == "" {
|
||
cond, condArgs := coupleCond(cid)
|
||
if cid == nil {
|
||
cond += " AND user_id = ?"
|
||
condArgs = append(condArgs, uid)
|
||
}
|
||
cycArgs := append(append([]any{}, condArgs...), recordDate, recordDate)
|
||
cycles, err := c.DB.Query("SELECT id, start_date, end_date FROM period_records WHERE "+cond+" AND start_date <= ? AND end_date >= ?", cycArgs...)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
for _, cy := range cycles {
|
||
cntArgs := append(append([]any{}, condArgs...), cy["start_date"], cy["end_date"])
|
||
cnt, err := c.DB.QueryOne("SELECT COUNT(*) as cnt FROM period_daily_records WHERE "+cond+" AND record_date >= ? AND record_date <= ? AND period > 0", cntArgs...)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
if cnt != nil && cnt.Int("cnt") == 0 {
|
||
delArgs := append([]any{cy.Int("id")}, condArgs...)
|
||
if _, err := c.DB.Exec("DELETE FROM period_records WHERE id = ? AND "+cond, delArgs...); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return c.OK(M{"success": true})
|
||
}
|
||
|
||
func periodDailyGet(c *httpapi.Ctx) error {
|
||
cid := cidOrNil(c)
|
||
uid := c.UID()
|
||
date := strings.TrimSpace(c.Q("record_date"))
|
||
if !store.Truthy(date) {
|
||
return c.Err(400, "请选择日期")
|
||
}
|
||
cond, condArgs := coupleCond(cid)
|
||
if cid == nil {
|
||
cond += " AND user_id = ?"
|
||
condArgs = append(condArgs, uid)
|
||
}
|
||
args := append(append([]any{}, condArgs...), date)
|
||
rec, err := c.DB.QueryOne("SELECT * FROM period_daily_records WHERE "+cond+" AND record_date = ?", args...)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
if rec == nil {
|
||
return c.OK([]any{})
|
||
}
|
||
return c.OK(rec)
|
||
}
|
||
|
||
func periodSexAdd(c *httpapi.Ctx) error {
|
||
if err := c.RequirePOST(); err != nil {
|
||
return err
|
||
}
|
||
cid := cidOrNil(c)
|
||
uid := c.UID()
|
||
recordDate := c.InTrim("record_date")
|
||
if !store.Truthy(recordDate) {
|
||
return c.Err(400, "请选择日期")
|
||
}
|
||
recordTime := c.InTrim("record_time")
|
||
measure := c.InTrim("measure")
|
||
measureCustom := c.InTrim("measure_custom")
|
||
if err := checkTextOr400(c, measureCustom); err != nil {
|
||
return err
|
||
}
|
||
var rt any
|
||
if store.Truthy(recordTime) {
|
||
rt = recordTime
|
||
}
|
||
recID, err := c.DB.Insert("INSERT INTO period_sex_records (couple_id, user_id, record_date, record_time, measure, measure_custom) VALUES (?, ?, ?, ?, ?, ?)",
|
||
cid, uid, recordDate, rt, measure, measureCustom)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
desc := "记录了亲密行为"
|
||
if store.Truthy(recordTime) {
|
||
desc += "(" + recordDate + " " + recordTime + ")"
|
||
}
|
||
actID, err := logActivityErr(c, cid, uid, "period_sex_add", desc)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
if partnerID := partnerUserId(c.DB, uid); partnerID != 0 {
|
||
if periodVisible(c, uid) != 0 {
|
||
c.App.Wechat.SendSubscribeMessage(partnerID, "notify", map[string]string{
|
||
"thing1": "亲密记录有更新", "thing2": "TA新增了一条亲密记录 💕", "date3": recordDate, "thing4": "点击查看详情",
|
||
}, "pages/period/period", actID, uid)
|
||
}
|
||
}
|
||
return c.OK(M{"success": true, "id": fmt.Sprint(recID)})
|
||
}
|
||
|
||
func periodSexDelete(c *httpapi.Ctx) error {
|
||
if err := c.RequirePOST(); err != nil {
|
||
return err
|
||
}
|
||
cid := cidOrNil(c)
|
||
uid := c.UID()
|
||
id := c.InInt("id")
|
||
if id == 0 {
|
||
return c.Err(400, "缺少记录ID")
|
||
}
|
||
if cid == nil {
|
||
if _, err := c.DB.Exec("DELETE FROM period_sex_records WHERE id = ? AND couple_id IS NULL AND user_id = ?", id, uid); err != nil {
|
||
return err
|
||
}
|
||
} else {
|
||
if _, err := c.DB.Exec("DELETE FROM period_sex_records WHERE id = ? AND couple_id = ?", id, cid); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
return c.OK(M{"success": true})
|
||
}
|
||
|
||
func periodSexList(c *httpapi.Ctx) error {
|
||
cid := cidOrNil(c)
|
||
uid := c.UID()
|
||
date := strings.TrimSpace(c.Q("record_date"))
|
||
if !store.Truthy(date) {
|
||
return c.Err(400, "请选择日期")
|
||
}
|
||
cond, condArgs := coupleCond(cid)
|
||
if cid == nil {
|
||
cond += " AND user_id = ?"
|
||
condArgs = append(condArgs, uid)
|
||
}
|
||
args := append(append([]any{}, condArgs...), date)
|
||
items, err := c.DB.Query("SELECT * FROM period_sex_records WHERE "+cond+" AND record_date = ? ORDER BY record_time ASC, id ASC", args...)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return c.OK(rows(items))
|
||
}
|