后端:资料距离、语音图片会员限制、免短信注册、AI 托管回复修复
- 单用户资料接口补上距离:此前只有推荐/附近列表会算距离,资料页和 聊天头部因此无内容可显示。沿用同一套 haversine 与隐私开关。 - 语音/图片消息可限定会员发送,两个开关在管理端「运营配置」中修改 (迁移 032)。校验放在 persistMessageContext,HTTP 与 WebSocket 两条发送路径都覆盖;文本消息永不受限。 - 短信服务关闭时注册不再要求验证码:关掉之后没人能拿到验证码,继续 要求就等于关闭注册通道。重置密码不做同样放宽,那里缺验证码等于 凭手机号夺号。app/config 增加 smsVerification 供客户端决定表单形态。 - 修复 AI 托管账号之间不回复:原规则按「发送方是否托管账号」拦截, 把真人操作测试号的正常对话也挡了。改为标记 worker 自己写入的回复, 只对 AI 生成的消息跳过入队。 - ai.default_model_id 同时接受模型 ID 与名称,填名称时不再被 MySQL 静默转成 0 而使配置失效。 - 聊天媒体留存管理与清理任务(迁移 033,两台线上均已应用)。 新增集成测试均针对真实 MySQL:会员限制、免短信注册、AI 入队规则、 默认模型解析、资料距离。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
842990b0e7
commit
acd8933dbb
@@ -75,7 +75,12 @@ func (a *App) membershipStatus(w http.ResponseWriter, r *http.Request) {
|
||||
likeRemaining = 0
|
||||
}
|
||||
}
|
||||
entitlementView := map[string]any{"dailyActiveChatLimit": entitlements.DailyActiveChatLimit, "dailyLikeLimit": entitlements.DailyLikeLimit, "dailyLikeUsed": likeUsed, "dailyLikeRemaining": likeRemaining, "canViewVisitors": entitlements.CanViewVisitors, "canInvisibleVisit": entitlements.CanInvisibleVisit, "recommendationWeight": entitlements.RecommendationWeight}
|
||||
// The chat page reads these two to explain the lock before a recording or an
|
||||
// upload is made, instead of letting the send fail after the work is done.
|
||||
_, voiceGated := a.messageMembershipGate(r.Context(), messageTypeVoice)
|
||||
_, imageGated := a.messageMembershipGate(r.Context(), messageTypeImage)
|
||||
member := a.hasActiveMembership(r.Context(), current(r).ID)
|
||||
entitlementView := map[string]any{"dailyActiveChatLimit": entitlements.DailyActiveChatLimit, "dailyLikeLimit": entitlements.DailyLikeLimit, "dailyLikeUsed": likeUsed, "dailyLikeRemaining": likeRemaining, "canViewVisitors": entitlements.CanViewVisitors, "canInvisibleVisit": entitlements.CanInvisibleVisit, "recommendationWeight": entitlements.RecommendationWeight, "voiceMessageRequiresVip": voiceGated, "imageMessageRequiresVip": imageGated, "canSendVoiceMessage": member || !voiceGated, "canSendImageMessage": member || !imageGated}
|
||||
var planName string
|
||||
var level int
|
||||
var expires time.Time
|
||||
@@ -339,6 +344,9 @@ func (a *App) appConfig(w http.ResponseWriter, r *http.Request) {
|
||||
"feed": a.configBool(r.Context(), "app.features.feed", true),
|
||||
"membership": a.configBool(r.Context(), "app.features.membership", true),
|
||||
"im": a.configBool(r.Context(), "app.features.im", true),
|
||||
// The sign-up form hides its code field when this is off, instead of
|
||||
// asking for something the server will never send.
|
||||
"smsVerification": a.smsVerificationRequired(r.Context()),
|
||||
}
|
||||
reply(w, map[string]any{"configs": configs, "platform": platform, "features": features, "maintenance": map[string]any{"enabled": a.configBool(r.Context(), "app.maintenance.enabled", false), "message": a.configPlain(r.Context(), "app.maintenance.message", "系统维护中,请稍后再试")}, "legal": map[string]string{"userAgreementVersion": a.configPlain(r.Context(), "legal.user_agreement_version", "1.0"), "privacyPolicyVersion": a.configPlain(r.Context(), "legal.privacy_policy_version", "1.0"), "operatorName": a.configPlain(r.Context(), "legal.operator_name", ""), "contact": a.configPlain(r.Context(), "legal.contact", ""), "effectiveDate": a.configPlain(r.Context(), "legal.effective_date", ""), "userAgreementUrl": a.configPlain(r.Context(), "legal.user_agreement_url", ""), "privacyPolicyUrl": a.configPlain(r.Context(), "legal.privacy_policy_url", "")}, "minVersion": a.configPlain(r.Context(), "app.min_version."+platform, "1.0.0"), "latest": latest})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user