feat: require compliance confirmation for paid features

Gate payment, redemption, subscription, and invitation reward flows behind an audited compliance acknowledgement.
This commit is contained in:
CaIon 2026-05-13 22:18:46 +08:00
parent aa56667b8f
commit 0526a22643
No known key found for this signature in database
GPG Key ID: 0CFA613529A9921D
57 changed files with 1806 additions and 216 deletions

View File

@ -3,9 +3,11 @@ package controller
import (
"fmt"
"net/http"
"strconv"
"strings"
"github.com/QuantumNous/new-api/common"
"github.com/QuantumNous/new-api/i18n"
"github.com/QuantumNous/new-api/model"
"github.com/QuantumNous/new-api/setting"
"github.com/QuantumNous/new-api/setting/console_setting"
@ -27,6 +29,19 @@ var completionRatioMetaOptionKeys = []string{
"AudioCompletionRatio",
}
func isPaymentComplianceOptionKey(key string) bool {
return strings.HasPrefix(key, "payment_setting.compliance_")
}
func isPositiveOptionValue(value string) bool {
intValue, err := strconv.Atoi(strings.TrimSpace(value))
if err == nil {
return intValue > 0
}
floatValue, err := strconv.ParseFloat(strings.TrimSpace(value), 64)
return err == nil && floatValue > 0
}
func isVisiblePublicKeyOption(key string) bool {
switch key {
case "WaffoPancakeWebhookPublicKey", "WaffoPancakeWebhookTestKey":
@ -104,7 +119,6 @@ func GetOptions(c *gin.Context) {
"message": "",
"data": options,
})
return
}
type OptionUpdateRequest struct {
@ -133,6 +147,18 @@ func UpdateOption(c *gin.Context) {
option.Value = fmt.Sprintf("%v", option.Value)
}
switch option.Key {
case "QuotaForInviter", "QuotaForInvitee":
if isPositiveOptionValue(option.Value.(string)) && !operation_setting.IsPaymentComplianceConfirmed() {
common.ApiErrorI18n(c, i18n.MsgPaymentComplianceRequired)
return
}
default:
if isPaymentComplianceOptionKey(option.Key) {
common.ApiErrorMsg(c, "合规确认字段不允许通过通用设置接口修改")
return
}
}
switch option.Key {
case "GitHubOAuthEnabled":
if option.Value == "true" && common.GitHubClientId == "" {
c.JSON(http.StatusOK, gin.H{
@ -324,5 +350,4 @@ func UpdateOption(c *gin.Context) {
"success": true,
"message": "",
})
return
}

View File

@ -0,0 +1,82 @@
package controller
import (
"fmt"
"net/http"
"strconv"
"time"
"github.com/QuantumNous/new-api/common"
"github.com/QuantumNous/new-api/i18n"
"github.com/QuantumNous/new-api/logger"
"github.com/QuantumNous/new-api/model"
"github.com/QuantumNous/new-api/setting/operation_setting"
"github.com/gin-gonic/gin"
)
type PaymentComplianceRequest struct {
Confirmed bool `json:"confirmed"`
}
func requirePaymentCompliance(c *gin.Context) bool {
if !operation_setting.IsPaymentComplianceConfirmed() {
common.ApiErrorI18n(c, i18n.MsgPaymentComplianceRequired)
return false
}
return true
}
func ConfirmPaymentCompliance(c *gin.Context) {
if c.GetBool("use_access_token") {
c.JSON(http.StatusForbidden, gin.H{
"success": false,
"message": "This operation requires dashboard session authentication. API access token is not allowed.",
})
return
}
var req PaymentComplianceRequest
if err := common.DecodeJson(c.Request.Body, &req); err != nil {
common.ApiErrorMsg(c, "参数错误")
return
}
if !req.Confirmed {
common.ApiErrorMsg(c, "请确认合规声明")
return
}
now := time.Now().Unix()
userId := c.GetInt("id")
clientIP := c.ClientIP()
updates := map[string]string{
"payment_setting.compliance_confirmed": "true",
"payment_setting.compliance_terms_version": operation_setting.CurrentComplianceTermsVersion,
"payment_setting.compliance_confirmed_at": strconv.FormatInt(now, 10),
"payment_setting.compliance_confirmed_by": strconv.Itoa(userId),
"payment_setting.compliance_confirmed_ip": clientIP,
}
for key, value := range updates {
if err := model.UpdateOption(key, value); err != nil {
common.ApiError(c, err)
return
}
}
logger.LogInfo(c.Request.Context(), fmt.Sprintf(
"payment compliance confirmed user_id=%d ip=%s terms_version=%s confirmed_at=%d",
userId,
clientIP,
operation_setting.CurrentComplianceTermsVersion,
now,
))
common.ApiSuccess(c, gin.H{
"confirmed": true,
"terms_version": operation_setting.CurrentComplianceTermsVersion,
"confirmed_at": now,
"confirmed_by": userId,
})
}

View File

@ -7,7 +7,14 @@ import (
"github.com/QuantumNous/new-api/setting/operation_setting"
)
func isPaymentComplianceConfirmed() bool {
return operation_setting.IsPaymentComplianceConfirmed()
}
func isStripeTopUpEnabled() bool {
if !isPaymentComplianceConfirmed() {
return false
}
return strings.TrimSpace(setting.StripeApiSecret) != "" &&
strings.TrimSpace(setting.StripeWebhookSecret) != "" &&
strings.TrimSpace(setting.StripePriceId) != ""
@ -22,6 +29,9 @@ func isStripeWebhookEnabled() bool {
}
func isCreemTopUpEnabled() bool {
if !isPaymentComplianceConfirmed() {
return false
}
products := strings.TrimSpace(setting.CreemProducts)
return strings.TrimSpace(setting.CreemApiKey) != "" &&
products != "" &&
@ -37,6 +47,9 @@ func isCreemWebhookEnabled() bool {
}
func isWaffoTopUpEnabled() bool {
if !isPaymentComplianceConfirmed() {
return false
}
if !setting.WaffoEnabled {
return false
}
@ -61,6 +74,9 @@ func isWaffoWebhookEnabled() bool {
}
func isWaffoPancakeTopUpEnabled() bool {
if !isPaymentComplianceConfirmed() {
return false
}
if !setting.WaffoPancakeEnabled {
return false
}
@ -86,6 +102,9 @@ func isWaffoPancakeWebhookEnabled() bool {
}
func isEpayTopUpEnabled() bool {
if !isPaymentComplianceConfirmed() {
return false
}
return isEpayWebhookConfigured() && len(operation_setting.PayMethods) > 0
}

View File

@ -8,7 +8,21 @@ import (
"github.com/stretchr/testify/require"
)
func confirmPaymentComplianceForTest(t *testing.T) {
t.Helper()
paymentSetting := operation_setting.GetPaymentSetting()
originalConfirmed := paymentSetting.ComplianceConfirmed
originalTermsVersion := paymentSetting.ComplianceTermsVersion
t.Cleanup(func() {
paymentSetting.ComplianceConfirmed = originalConfirmed
paymentSetting.ComplianceTermsVersion = originalTermsVersion
})
paymentSetting.ComplianceConfirmed = true
paymentSetting.ComplianceTermsVersion = operation_setting.CurrentComplianceTermsVersion
}
func TestStripeWebhookEnabledRequiresTopUpAndWebhookConfig(t *testing.T) {
confirmPaymentComplianceForTest(t)
originalAPISecret := setting.StripeApiSecret
originalWebhookSecret := setting.StripeWebhookSecret
originalPriceID := setting.StripePriceId
@ -31,6 +45,7 @@ func TestStripeWebhookEnabledRequiresTopUpAndWebhookConfig(t *testing.T) {
}
func TestCreemWebhookEnabledRequiresTopUpAndWebhookConfig(t *testing.T) {
confirmPaymentComplianceForTest(t)
originalAPIKey := setting.CreemApiKey
originalProducts := setting.CreemProducts
originalWebhookSecret := setting.CreemWebhookSecret
@ -53,6 +68,7 @@ func TestCreemWebhookEnabledRequiresTopUpAndWebhookConfig(t *testing.T) {
}
func TestWaffoWebhookEnabledRequiresTopUpAndWebhookConfig(t *testing.T) {
confirmPaymentComplianceForTest(t)
originalEnabled := setting.WaffoEnabled
originalSandbox := setting.WaffoSandbox
originalAPIKey := setting.WaffoApiKey
@ -97,6 +113,7 @@ func TestWaffoWebhookEnabledRequiresTopUpAndWebhookConfig(t *testing.T) {
}
func TestWaffoPancakeWebhookEnabledRequiresTopUpAndWebhookConfig(t *testing.T) {
confirmPaymentComplianceForTest(t)
originalEnabled := setting.WaffoPancakeEnabled
originalSandbox := setting.WaffoPancakeSandbox
originalMerchantID := setting.WaffoPancakeMerchantID
@ -141,6 +158,7 @@ func TestWaffoPancakeWebhookEnabledRequiresTopUpAndWebhookConfig(t *testing.T) {
}
func TestEpayWebhookEnabledRequiresTopUpAndWebhookConfig(t *testing.T) {
confirmPaymentComplianceForTest(t)
originalPayAddress := operation_setting.PayAddress
originalEpayID := operation_setting.EpayId
originalEpayKey := operation_setting.EpayKey

View File

@ -8,6 +8,7 @@ import (
"github.com/QuantumNous/new-api/common"
"github.com/QuantumNous/new-api/i18n"
"github.com/QuantumNous/new-api/model"
"github.com/QuantumNous/new-api/setting/operation_setting"
"github.com/gin-gonic/gin"
)
@ -59,6 +60,11 @@ func GetRedemption(c *gin.Context) {
}
func AddRedemption(c *gin.Context) {
if !operation_setting.IsPaymentComplianceConfirmed() {
common.ApiErrorI18n(c, i18n.MsgPaymentComplianceRequired)
return
}
redemption := model.Redemption{}
err := c.ShouldBindJSON(&redemption)
if err != nil {

View File

@ -6,6 +6,7 @@ import (
"github.com/QuantumNous/new-api/common"
"github.com/QuantumNous/new-api/model"
"github.com/QuantumNous/new-api/setting/operation_setting"
"github.com/QuantumNous/new-api/setting/ratio_setting"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
@ -24,6 +25,11 @@ type BillingPreferenceRequest struct {
// ---- User APIs ----
func GetSubscriptionPlans(c *gin.Context) {
if !operation_setting.IsPaymentComplianceConfirmed() {
common.ApiSuccess(c, []SubscriptionPlanDTO{})
return
}
var plans []model.SubscriptionPlan
if err := model.DB.Where("enabled = ?", true).Order("sort_order desc, id desc").Find(&plans).Error; err != nil {
common.ApiError(c, err)
@ -108,6 +114,10 @@ type AdminUpsertSubscriptionPlanRequest struct {
}
func AdminCreateSubscriptionPlan(c *gin.Context) {
if !requirePaymentCompliance(c) {
return
}
var req AdminUpsertSubscriptionPlanRequest
if err := c.ShouldBindJSON(&req); err != nil {
common.ApiErrorMsg(c, "参数错误")
@ -166,6 +176,10 @@ func AdminCreateSubscriptionPlan(c *gin.Context) {
}
func AdminUpdateSubscriptionPlan(c *gin.Context) {
if !requirePaymentCompliance(c) {
return
}
id, _ := strconv.Atoi(c.Param("id"))
if id <= 0 {
common.ApiErrorMsg(c, "无效的ID")
@ -259,6 +273,10 @@ type AdminUpdateSubscriptionPlanStatusRequest struct {
}
func AdminUpdateSubscriptionPlanStatus(c *gin.Context) {
if !requirePaymentCompliance(c) {
return
}
id, _ := strconv.Atoi(c.Param("id"))
if id <= 0 {
common.ApiErrorMsg(c, "无效的ID")
@ -283,6 +301,10 @@ type AdminBindSubscriptionRequest struct {
}
func AdminBindSubscription(c *gin.Context) {
if !requirePaymentCompliance(c) {
return
}
var req AdminBindSubscriptionRequest
if err := c.ShouldBindJSON(&req); err != nil || req.UserId <= 0 || req.PlanId <= 0 {
common.ApiErrorMsg(c, "参数错误")
@ -322,6 +344,10 @@ type AdminCreateUserSubscriptionRequest struct {
// AdminCreateUserSubscription creates a new user subscription from a plan (no payment).
func AdminCreateUserSubscription(c *gin.Context) {
if !requirePaymentCompliance(c) {
return
}
userId, _ := strconv.Atoi(c.Param("id"))
if userId <= 0 {
common.ApiErrorMsg(c, "无效的用户ID")

View File

@ -21,6 +21,10 @@ type SubscriptionCreemPayRequest struct {
}
func SubscriptionRequestCreemPay(c *gin.Context) {
if !requirePaymentCompliance(c) {
return
}
var req SubscriptionCreemPayRequest
// Keep body for debugging consistency (like RequestCreemPay)

View File

@ -22,6 +22,10 @@ type SubscriptionEpayPayRequest struct {
}
func SubscriptionRequestEpay(c *gin.Context) {
if !requirePaymentCompliance(c) {
return
}
var req SubscriptionEpayPayRequest
if err := c.ShouldBindJSON(&req); err != nil || req.PlanId <= 0 {
common.ApiErrorMsg(c, "参数错误")

View File

@ -21,6 +21,10 @@ type SubscriptionStripePayRequest struct {
}
func SubscriptionRequestStripePay(c *gin.Context) {
if !requirePaymentCompliance(c) {
return
}
var req SubscriptionStripePayRequest
if err := c.ShouldBindJSON(&req); err != nil || req.PlanId <= 0 {
common.ApiErrorMsg(c, "参数错误")

View File

@ -22,8 +22,13 @@ import (
)
func GetTopUpInfo(c *gin.Context) {
complianceConfirmed := operation_setting.IsPaymentComplianceConfirmed()
// 获取支付方式
payMethods := operation_setting.PayMethods
if !complianceConfirmed {
payMethods = []map[string]string{}
}
// 如果启用了 Stripe 支付,添加到支付方法列表
if isStripeTopUpEnabled() {
@ -90,11 +95,14 @@ func GetTopUpInfo(c *gin.Context) {
}
data := gin.H{
"enable_online_topup": isEpayTopUpEnabled(),
"enable_stripe_topup": isStripeTopUpEnabled(),
"enable_creem_topup": isCreemTopUpEnabled(),
"enable_waffo_topup": enableWaffo,
"enable_waffo_pancake_topup": enableWaffoPancake,
"enable_online_topup": isEpayTopUpEnabled(),
"enable_stripe_topup": isStripeTopUpEnabled(),
"enable_creem_topup": isCreemTopUpEnabled(),
"enable_waffo_topup": enableWaffo,
"enable_waffo_pancake_topup": enableWaffoPancake,
"enable_redemption": complianceConfirmed,
"payment_compliance_confirmed": complianceConfirmed,
"payment_compliance_terms_version": operation_setting.CurrentComplianceTermsVersion,
"waffo_pay_methods": func() interface{} {
if enableWaffo {
return setting.GetWaffoPayMethods()

View File

@ -17,6 +17,7 @@ import (
"github.com/QuantumNous/new-api/model"
"github.com/QuantumNous/new-api/service"
"github.com/QuantumNous/new-api/setting"
"github.com/QuantumNous/new-api/setting/operation_setting"
"github.com/QuantumNous/new-api/constant"
@ -327,6 +328,10 @@ type TransferAffQuotaRequest struct {
}
func TransferAffQuota(c *gin.Context) {
if !requirePaymentCompliance(c) {
return
}
id := c.GetInt("id")
user, err := model.GetUserById(id, true)
if err != nil {
@ -1081,6 +1086,11 @@ func getTopUpLock(userID int) *topUpTryLock {
}
func TopUp(c *gin.Context) {
if !operation_setting.IsPaymentComplianceConfirmed() {
common.ApiErrorI18n(c, i18n.MsgPaymentComplianceRequired)
return
}
id := c.GetInt("id")
lock := getTopUpLock(id)
if !lock.TryLock() {

View File

@ -142,16 +142,17 @@ const (
// Payment related messages
const (
MsgPaymentNotConfigured = "payment.not_configured"
MsgPaymentMethodNotExists = "payment.method_not_exists"
MsgPaymentCallbackError = "payment.callback_error"
MsgPaymentCreateFailed = "payment.create_failed"
MsgPaymentStartFailed = "payment.start_failed"
MsgPaymentAmountTooLow = "payment.amount_too_low"
MsgPaymentStripeNotConfig = "payment.stripe_not_configured"
MsgPaymentWebhookNotConfig = "payment.webhook_not_configured"
MsgPaymentPriceIdNotConfig = "payment.price_id_not_configured"
MsgPaymentCreemNotConfig = "payment.creem_not_configured"
MsgPaymentNotConfigured = "payment.not_configured"
MsgPaymentMethodNotExists = "payment.method_not_exists"
MsgPaymentCallbackError = "payment.callback_error"
MsgPaymentCreateFailed = "payment.create_failed"
MsgPaymentStartFailed = "payment.start_failed"
MsgPaymentAmountTooLow = "payment.amount_too_low"
MsgPaymentStripeNotConfig = "payment.stripe_not_configured"
MsgPaymentWebhookNotConfig = "payment.webhook_not_configured"
MsgPaymentPriceIdNotConfig = "payment.price_id_not_configured"
MsgPaymentCreemNotConfig = "payment.creem_not_configured"
MsgPaymentComplianceRequired = "payment.compliance_required"
)
// Topup related messages

View File

@ -134,6 +134,7 @@ payment.stripe_not_configured: "Stripe is not configured or key is invalid"
payment.webhook_not_configured: "Webhook is not configured"
payment.price_id_not_configured: "StripePriceId is not configured for this plan"
payment.creem_not_configured: "CreemProductId is not configured for this plan"
payment.compliance_required: "Payment, redemption, subscription, and invitation reward features are disabled. The administrator must confirm compliance terms before enabling them."
# Topup messages
topup.not_provided: "Payment order number not provided"

View File

@ -135,6 +135,7 @@ payment.stripe_not_configured: "Stripe 未配置或密钥无效"
payment.webhook_not_configured: "Webhook 未配置"
payment.price_id_not_configured: "该套餐未配置 StripePriceId"
payment.creem_not_configured: "该套餐未配置 CreemProductId"
payment.compliance_required: "支付、兑换码、订阅计划和邀请返利功能已禁用。管理员需先确认合规声明后方可启用。"
# Topup messages
topup.not_provided: "未提供支付单号"

View File

@ -135,6 +135,7 @@ payment.stripe_not_configured: "Stripe 未設定或密鑰無效"
payment.webhook_not_configured: "Webhook 未設定"
payment.price_id_not_configured: "該訂閱方案未設定 StripePriceId"
payment.creem_not_configured: "該訂閱方案未設定 CreemProductId"
payment.compliance_required: "支付、兌換碼、訂閱方案和邀請返利功能已停用。管理員需先確認合規聲明後方可啟用。"
# Topup messages
topup.not_provided: "未提供支付單號"

View File

@ -11,6 +11,7 @@ import (
"github.com/QuantumNous/new-api/common"
"github.com/QuantumNous/new-api/dto"
"github.com/QuantumNous/new-api/logger"
"github.com/QuantumNous/new-api/setting/operation_setting"
"github.com/bytedance/gopkg/util/gopool"
"gorm.io/gorm"
@ -420,7 +421,7 @@ func (user *User) Insert(inviterId int) error {
if common.QuotaForNewUser > 0 {
RecordLog(user.Id, LogTypeSystem, fmt.Sprintf("新用户注册赠送 %s", logger.LogQuota(common.QuotaForNewUser)))
}
if inviterId != 0 {
if inviterId != 0 && operation_setting.IsPaymentComplianceConfirmed() {
if common.QuotaForInvitee > 0 {
_ = IncreaseUserQuota(user.Id, common.QuotaForInvitee, true)
RecordLog(user.Id, LogTypeSystem, fmt.Sprintf("使用邀请码赠送 %s", logger.LogQuota(common.QuotaForInvitee)))
@ -481,7 +482,7 @@ func (user *User) FinalizeOAuthUserCreation(inviterId int) {
if common.QuotaForNewUser > 0 {
RecordLog(user.Id, LogTypeSystem, fmt.Sprintf("新用户注册赠送 %s", logger.LogQuota(common.QuotaForNewUser)))
}
if inviterId != 0 {
if inviterId != 0 && operation_setting.IsPaymentComplianceConfirmed() {
if common.QuotaForInvitee > 0 {
_ = IncreaseUserQuota(user.Id, common.QuotaForInvitee, true)
RecordLog(user.Id, LogTypeSystem, fmt.Sprintf("使用邀请码赠送 %s", logger.LogQuota(common.QuotaForInvitee)))

View File

@ -181,6 +181,7 @@ func SetApiRouter(router *gin.Engine) {
{
optionRoute.GET("/", controller.GetOptions)
optionRoute.PUT("/", controller.UpdateOption)
optionRoute.POST("/payment_compliance", controller.ConfirmPaymentCompliance)
optionRoute.GET("/channel_affinity_cache", controller.GetChannelAffinityCacheStats)
optionRoute.DELETE("/channel_affinity_cache", controller.ClearChannelAffinityCache)
optionRoute.POST("/rest_model_ratio", controller.ResetModelRatio)

View File

@ -5,8 +5,16 @@ import "github.com/QuantumNous/new-api/setting/config"
type PaymentSetting struct {
AmountOptions []int `json:"amount_options"`
AmountDiscount map[int]float64 `json:"amount_discount"` // 充值金额对应的折扣,例如 100 元 0.9 表示 100 元充值享受 9 折优惠
ComplianceConfirmed bool `json:"compliance_confirmed"`
ComplianceTermsVersion string `json:"compliance_terms_version"`
ComplianceConfirmedAt int64 `json:"compliance_confirmed_at"`
ComplianceConfirmedBy int `json:"compliance_confirmed_by"`
ComplianceConfirmedIP string `json:"compliance_confirmed_ip"`
}
const CurrentComplianceTermsVersion = "v1"
// 默认配置
var paymentSetting = PaymentSetting{
AmountOptions: []int{10, 20, 50, 100, 200, 500},
@ -21,3 +29,8 @@ func init() {
func GetPaymentSetting() *PaymentSetting {
return &paymentSetting
}
func IsPaymentComplianceConfirmed() bool {
return paymentSetting.ComplianceConfirmed &&
paymentSetting.ComplianceTermsVersion == CurrentComplianceTermsVersion
}

View File

@ -62,6 +62,7 @@ const RiskAcknowledgementModal = React.memo(function RiskAcknowledgementModal({
checklist = [],
inputPrompt = '',
requiredText = '',
requiredTextParts = [],
inputPlaceholder = '',
mismatchText = '',
cancelText = '',
@ -72,24 +73,68 @@ const RiskAcknowledgementModal = React.memo(function RiskAcknowledgementModal({
const isMobile = useIsMobile();
const [checkedItems, setCheckedItems] = useState([]);
const [typedText, setTypedText] = useState('');
const [typedTextParts, setTypedTextParts] = useState([]);
const normalizedRequiredTextParts = useMemo(() => {
let inputIndex = 0;
return requiredTextParts.map((part) => {
if (part.type === 'input') {
const normalizedPart = { ...part, inputIndex };
inputIndex += 1;
return normalizedPart;
}
return part;
});
}, [requiredTextParts]);
const requiredTextInputCount = useMemo(
() =>
normalizedRequiredTextParts.filter((part) => part.type === 'input')
.length,
[normalizedRequiredTextParts],
);
const hasSegmentedRequiredText = requiredTextInputCount > 0;
const requiredTextToDisplay = hasSegmentedRequiredText
? normalizedRequiredTextParts.map((part) => part.text).join('')
: requiredText;
useEffect(() => {
if (!visible) return;
setCheckedItems(Array(checklist.length).fill(false));
setTypedText('');
}, [visible, checklist.length]);
setTypedTextParts(Array(requiredTextInputCount).fill(''));
}, [visible, checklist.length, requiredTextInputCount]);
const allChecked = useMemo(() => {
if (checklist.length === 0) return true;
return checkedItems.length === checklist.length && checkedItems.every(Boolean);
return (
checkedItems.length === checklist.length && checkedItems.every(Boolean)
);
}, [checkedItems, checklist.length]);
const typedMatched = useMemo(() => {
if (hasSegmentedRequiredText) {
return normalizedRequiredTextParts.every((part) => {
if (part.type === 'static') return true;
return (
typedTextParts[part.inputIndex ?? 0]?.trim() === part.text.trim()
);
});
}
if (!requiredText) return true;
return typedText.trim() === requiredText.trim();
}, [typedText, requiredText]);
}, [
hasSegmentedRequiredText,
normalizedRequiredTextParts,
requiredText,
typedText,
typedTextParts,
]);
const detailText = useMemo(() => detailItems.join(', '), [detailItems]);
const hasTypedRequiredText = hasSegmentedRequiredText
? typedTextParts.some((part) => part.trim() !== '')
: typedText.length > 0;
const canConfirm = allChecked && typedMatched;
const handleChecklistChange = useCallback((index, checked) => {
@ -100,6 +145,14 @@ const RiskAcknowledgementModal = React.memo(function RiskAcknowledgementModal({
});
}, []);
const handleTextPartChange = useCallback((index, value) => {
setTypedTextParts((previous) => {
const next = [...previous];
next[index] = value;
return next;
});
}, []);
return (
<Modal
visible={visible}
@ -134,7 +187,6 @@ const RiskAcknowledgementModal = React.memo(function RiskAcknowledgementModal({
}
>
<div className='flex flex-col gap-4'>
<RiskMarkdownBlock markdownContent={markdownContent} />
{detailItems.length > 0 ? (
@ -176,7 +228,7 @@ const RiskAcknowledgementModal = React.memo(function RiskAcknowledgementModal({
</div>
) : null}
{requiredText ? (
{requiredTextToDisplay ? (
<div
className='flex flex-col gap-2 rounded-lg'
style={{
@ -187,19 +239,51 @@ const RiskAcknowledgementModal = React.memo(function RiskAcknowledgementModal({
>
{inputPrompt ? <Text strong>{inputPrompt}</Text> : null}
<div className='font-mono text-xs break-all rounded-md p-2 bg-gray-50 border border-gray-200'>
{requiredText}
{requiredTextToDisplay}
</div>
<Input
value={typedText}
onChange={setTypedText}
placeholder={inputPlaceholder}
autoFocus={visible}
onCopy={(event) => event.preventDefault()}
onCut={(event) => event.preventDefault()}
onPaste={(event) => event.preventDefault()}
onDrop={(event) => event.preventDefault()}
/>
{!typedMatched && typedText ? (
{hasSegmentedRequiredText ? (
<div className='flex flex-wrap items-center gap-2'>
{normalizedRequiredTextParts.map((part, index) =>
part.type === 'static' ? (
<span
key={`static-${index}`}
className='select-none rounded-md border border-gray-200 bg-white px-2 py-1 font-mono text-sm text-gray-500'
>
{part.text}
</span>
) : (
<Input
key={`input-${index}`}
value={typedTextParts[part.inputIndex ?? 0] ?? ''}
onChange={(value) =>
handleTextPartChange(part.inputIndex ?? 0, value)
}
placeholder={
part.placeholder || part.text || inputPlaceholder
}
autoFocus={visible && part.inputIndex === 0}
onCopy={(event) => event.preventDefault()}
onCut={(event) => event.preventDefault()}
onPaste={(event) => event.preventDefault()}
onDrop={(event) => event.preventDefault()}
style={{ width: isMobile ? '100%' : 260 }}
/>
),
)}
</div>
) : (
<Input
value={typedText}
onChange={setTypedText}
placeholder={inputPlaceholder}
autoFocus={visible}
onCopy={(event) => event.preventDefault()}
onCut={(event) => event.preventDefault()}
onPaste={(event) => event.preventDefault()}
onDrop={(event) => event.preventDefault()}
/>
)}
{!typedMatched && hasTypedRequiredText ? (
<Text type='danger' size='small'>
{mismatchText}
</Text>

View File

@ -18,15 +18,18 @@ For commercial licensing, please contact support@quantumnous.com
*/
import React, { useEffect, useState } from 'react';
import { Card, Spin, Tabs } from '@douyinfe/semi-ui';
import { Banner, Button, Card, Spin, Tabs } from '@douyinfe/semi-ui';
import SettingsGeneralPayment from '../../pages/Setting/Payment/SettingsGeneralPayment';
import SettingsPaymentGateway from '../../pages/Setting/Payment/SettingsPaymentGateway';
import SettingsPaymentGatewayStripe from '../../pages/Setting/Payment/SettingsPaymentGatewayStripe';
import SettingsPaymentGatewayCreem from '../../pages/Setting/Payment/SettingsPaymentGatewayCreem';
import SettingsPaymentGatewayWaffo from '../../pages/Setting/Payment/SettingsPaymentGatewayWaffo';
import SettingsPaymentGatewayWaffoPancake from '../../pages/Setting/Payment/SettingsPaymentGatewayWaffoPancake';
import { API, showError, toBoolean } from '../../helpers';
import { API, showError, showSuccess, toBoolean } from '../../helpers';
import { useTranslation } from 'react-i18next';
import RiskAcknowledgementModal from '../common/modals/RiskAcknowledgementModal';
const CURRENT_COMPLIANCE_TERMS_VERSION = 'v1';
const PaymentSetting = () => {
const { t } = useTranslation();
@ -60,9 +63,59 @@ const PaymentSetting = () => {
WaffoPancakeCurrency: 'USD',
WaffoPancakeUnitPrice: 1.0,
WaffoPancakeMinTopUp: 1,
'payment_setting.compliance_confirmed': false,
'payment_setting.compliance_terms_version': '',
'payment_setting.compliance_confirmed_at': 0,
'payment_setting.compliance_confirmed_by': 0,
});
let [loading, setLoading] = useState(false);
const [complianceVisible, setComplianceVisible] = useState(false);
const complianceStatements = [
t('你已合法取得所接入模型 API、账号、密钥和额度的授权'),
t(
'你承诺仅在已取得上游服务商、模型服务提供方或相关权利方合法授权的范围内使用其 API、账号、密钥、额度及服务能力不进行未经授权的转售、倒卖、分销或其他违规商业化使用。',
),
t(
'如向中华人民共和国境内公众提供生成式人工智能服务,你将依法履行备案登记、安全评估、内容安全、投诉举报、生成合成内容标识、日志留存、个人信息保护等合规义务;',
),
t(
'你承诺不会利用本系统实施、协助实施或变相实施违反适用法律法规、监管要求、平台规则、社会公共利益或第三方合法权益的行为。',
),
t('你理解并自行承担部署、运营和收费行为产生的法律责任。'),
t(
'你理解本合规提醒仅用于风险提示,不构成法律意见、合规审查结论或对你使用本系统行为合法性的保证;你应根据实际业务场景自行咨询专业法律或合规顾问。',
),
];
const requiredComplianceText = t(
'我已阅读并理解上述合规提醒,知悉相关法律风险,并确认自行承担部署、运营和收费行为产生的法律责任',
);
const requiredComplianceTextParts = [
{
type: 'input',
text: t('我已阅读并理解上述合规提醒'),
},
{ type: 'static', text: t('') },
{
type: 'input',
text: t('知悉相关法律风险'),
},
{ type: 'static', text: t('') },
{
type: 'input',
text: t('并确认自行承担部署'),
},
{ type: 'static', text: t('、') },
{
type: 'input',
text: t('运营和收费行为产生的法律责任'),
},
];
const complianceConfirmed =
inputs['payment_setting.compliance_confirmed'] &&
inputs['payment_setting.compliance_terms_version'] ===
CURRENT_COMPLIANCE_TERMS_VERSION;
const getOptions = async () => {
const res = await API.get('/api/option/');
@ -104,6 +157,16 @@ const PaymentSetting = () => {
newInputs['AmountDiscount'] = item.value;
}
break;
case 'payment_setting.compliance_confirmed':
newInputs[item.key] = toBoolean(item.value);
break;
case 'payment_setting.compliance_confirmed_at':
case 'payment_setting.compliance_confirmed_by':
newInputs[item.key] = parseInt(item.value) || 0;
break;
case 'payment_setting.compliance_terms_version':
newInputs[item.key] = item.value;
break;
case 'Price':
case 'MinTopUp':
case 'StripeUnitPrice':
@ -154,59 +217,143 @@ const PaymentSetting = () => {
onRefresh();
}, []);
const confirmCompliance = async () => {
try {
const res = await API.post('/api/option/payment_compliance', {
confirmed: true,
});
if (res.data.success) {
showSuccess(t('合规声明确认成功'));
setComplianceVisible(false);
await onRefresh();
} else {
showError(res.data.message || t('确认失败'));
}
} catch (error) {
showError(t('确认失败'));
}
};
return (
<>
<Spin spinning={loading} size='large'>
<Card style={{ marginTop: '10px' }}>
<Tabs
type='card'
defaultActiveKey='general'
contentStyle={{ paddingTop: 24 }}
{!complianceConfirmed ? (
<Banner
type='warning'
title={t('需要确认合规声明')}
description={
<div className='flex flex-col gap-2'>
<span>
{t(
'确认前,支付、兑换码、订阅计划和邀请返利功能将保持锁定。',
)}
</span>
<Button
type='warning'
theme='solid'
onClick={() => setComplianceVisible(true)}
>
{t('确认合规声明')}
</Button>
</div>
}
closeIcon={null}
style={{ marginBottom: 16 }}
fullMode={false}
/>
) : (
<Banner
type='success'
title={t('合规声明已确认')}
description={t('确认时间:{{time}},确认用户:#{{userId}}', {
time: inputs['payment_setting.compliance_confirmed_at']
? new Date(
inputs['payment_setting.compliance_confirmed_at'] * 1000,
).toLocaleString()
: '-',
userId:
inputs['payment_setting.compliance_confirmed_by'] || '-',
})}
closeIcon={null}
style={{ marginBottom: 16 }}
fullMode={false}
/>
)}
<div
style={
complianceConfirmed
? undefined
: { opacity: 0.4, pointerEvents: 'none' }
}
>
<Tabs.TabPane tab={t('通用设置')} itemKey='general'>
<SettingsGeneralPayment
options={inputs}
refresh={onRefresh}
hideSectionTitle
/>
</Tabs.TabPane>
<Tabs.TabPane tab={t('易支付设置')} itemKey='epay'>
<SettingsPaymentGateway
options={inputs}
refresh={onRefresh}
hideSectionTitle
/>
</Tabs.TabPane>
<Tabs.TabPane tab={t('Stripe 设置')} itemKey='stripe'>
<SettingsPaymentGatewayStripe
options={inputs}
refresh={onRefresh}
hideSectionTitle
/>
</Tabs.TabPane>
<Tabs.TabPane tab={t('Creem 设置')} itemKey='creem'>
<SettingsPaymentGatewayCreem
options={inputs}
refresh={onRefresh}
hideSectionTitle
/>
</Tabs.TabPane>
<Tabs.TabPane tab={t('Waffo 设置')} itemKey='waffo'>
<SettingsPaymentGatewayWaffo
options={inputs}
refresh={onRefresh}
hideSectionTitle
/>
</Tabs.TabPane>
{/*<Tabs.TabPane tab={t('Waffo Pancake 设置')} itemKey='waffo-pancake'>*/}
{/* <SettingsPaymentGatewayWaffoPancake*/}
{/* options={inputs}*/}
{/* refresh={onRefresh}*/}
{/* hideSectionTitle*/}
{/* />*/}
{/*</Tabs.TabPane>*/}
</Tabs>
<Tabs
type='card'
defaultActiveKey='general'
contentStyle={{ paddingTop: 24 }}
>
<Tabs.TabPane tab={t('通用设置')} itemKey='general'>
<SettingsGeneralPayment
options={inputs}
refresh={onRefresh}
hideSectionTitle
/>
</Tabs.TabPane>
<Tabs.TabPane tab={t('易支付设置')} itemKey='epay'>
<SettingsPaymentGateway
options={inputs}
refresh={onRefresh}
hideSectionTitle
/>
</Tabs.TabPane>
<Tabs.TabPane tab={t('Stripe 设置')} itemKey='stripe'>
<SettingsPaymentGatewayStripe
options={inputs}
refresh={onRefresh}
hideSectionTitle
/>
</Tabs.TabPane>
<Tabs.TabPane tab={t('Creem 设置')} itemKey='creem'>
<SettingsPaymentGatewayCreem
options={inputs}
refresh={onRefresh}
hideSectionTitle
/>
</Tabs.TabPane>
<Tabs.TabPane tab={t('Waffo 设置')} itemKey='waffo'>
<SettingsPaymentGatewayWaffo
options={inputs}
refresh={onRefresh}
hideSectionTitle
/>
</Tabs.TabPane>
{/*<Tabs.TabPane tab={t('Waffo Pancake 设置')} itemKey='waffo-pancake'>*/}
{/* <SettingsPaymentGatewayWaffoPancake*/}
{/* options={inputs}*/}
{/* refresh={onRefresh}*/}
{/* hideSectionTitle*/}
{/* />*/}
{/*</Tabs.TabPane>*/}
</Tabs>
</div>
</Card>
<RiskAcknowledgementModal
visible={complianceVisible}
title={t('确认合规声明')}
markdownContent={t(
'该操作将启用支付、兑换码、订阅计划和邀请返利相关功能。请仔细阅读并确认以下声明。',
)}
checklist={complianceStatements}
inputPrompt={t('请输入以下文字以确认:')}
requiredText={requiredComplianceText}
requiredTextParts={requiredComplianceTextParts}
inputPlaceholder={t('请输入确认文案')}
mismatchText={t('输入内容与要求文案不一致')}
cancelText={t('取消')}
confirmText={t('确认并启用')}
onCancel={() => setComplianceVisible(false)}
onConfirm={confirmCompliance}
/>
</Spin>
</>
);

View File

@ -20,7 +20,7 @@ For commercial licensing, please contact support@quantumnous.com
import React from 'react';
import { Button } from '@douyinfe/semi-ui';
const SubscriptionsActions = ({ openCreate, t }) => {
const SubscriptionsActions = ({ openCreate, t, disabled = false }) => {
return (
<div className='flex gap-2 w-full md:w-auto'>
<Button
@ -28,6 +28,7 @@ const SubscriptionsActions = ({ openCreate, t }) => {
className='w-full md:w-auto'
onClick={openCreate}
size='small'
disabled={disabled}
>
{t('新建套餐')}
</Button>

View File

@ -228,7 +228,11 @@ const renderPaymentConfig = (text, record, t, enableEpay) => {
);
};
const renderOperations = (text, record, { openEdit, setPlanEnabled, t }) => {
const renderOperations = (
text,
record,
{ openEdit, setPlanEnabled, t, complianceConfirmed },
) => {
const isEnabled = record?.plan?.enabled;
const handleToggle = () => {
@ -256,11 +260,18 @@ const renderOperations = (text, record, { openEdit, setPlanEnabled, t }) => {
type='tertiary'
size='small'
onClick={() => openEdit(record)}
disabled={!complianceConfirmed}
>
{t('编辑')}
</Button>
{isEnabled ? (
<Button theme='light' type='danger' size='small' onClick={handleToggle}>
<Button
theme='light'
type='danger'
size='small'
onClick={handleToggle}
disabled={!complianceConfirmed}
>
{t('禁用')}
</Button>
) : (
@ -269,6 +280,7 @@ const renderOperations = (text, record, { openEdit, setPlanEnabled, t }) => {
type='primary'
size='small'
onClick={handleToggle}
disabled={!complianceConfirmed}
>
{t('启用')}
</Button>
@ -282,6 +294,7 @@ export const getSubscriptionsColumns = ({
openEdit,
setPlanEnabled,
enableEpay,
complianceConfirmed = true,
}) => {
return [
{
@ -351,7 +364,12 @@ export const getSubscriptionsColumns = ({
fixed: 'right',
width: 160,
render: (text, record) =>
renderOperations(text, record, { openEdit, setPlanEnabled, t }),
renderOperations(text, record, {
openEdit,
setPlanEnabled,
t,
complianceConfirmed,
}),
},
];
};

View File

@ -35,6 +35,7 @@ const SubscriptionsTable = (subscriptionsData) => {
setPlanEnabled,
t,
enableEpay,
complianceConfirmed,
} = subscriptionsData;
const columns = useMemo(() => {
@ -43,8 +44,9 @@ const SubscriptionsTable = (subscriptionsData) => {
openEdit,
setPlanEnabled,
enableEpay,
complianceConfirmed,
});
}, [t, openEdit, setPlanEnabled, enableEpay]);
}, [t, openEdit, setPlanEnabled, enableEpay, complianceConfirmed]);
const tableColumns = useMemo(() => {
return compactMode

View File

@ -17,7 +17,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import React, { useContext } from 'react';
import React, { useContext, useEffect, useState } from 'react';
import { Banner } from '@douyinfe/semi-ui';
import CardPro from '../../common/ui/CardPro';
import SubscriptionsTable from './SubscriptionsTable';
@ -28,12 +28,14 @@ import { useSubscriptionsData } from '../../../hooks/subscriptions/useSubscripti
import { useIsMobile } from '../../../hooks/common/useIsMobile';
import { createCardProPagination } from '../../../helpers/utils';
import { StatusContext } from '../../../context/Status';
import { API } from '../../../helpers';
const SubscriptionsPage = () => {
const subscriptionsData = useSubscriptionsData();
const isMobile = useIsMobile();
const [statusState] = useContext(StatusContext);
const enableEpay = !!statusState?.status?.enable_online_topup;
const [complianceConfirmed, setComplianceConfirmed] = useState(true);
const {
showEdit,
@ -47,6 +49,22 @@ const SubscriptionsPage = () => {
t,
} = subscriptionsData;
useEffect(() => {
const loadComplianceStatus = async () => {
try {
const res = await API.get('/api/user/topup/info');
if (res.data?.success) {
setComplianceConfirmed(
res.data.data?.payment_compliance_confirmed !== false,
);
}
} catch (error) {
// Keep the page usable if status loading fails; backend still enforces.
}
};
loadComplianceStatus();
}, []);
return (
<>
<AddEditSubscriptionModal
@ -71,7 +89,11 @@ const SubscriptionsPage = () => {
<div className='flex flex-col md:flex-row justify-between items-start md:items-center gap-2 w-full'>
{/* Mobile: actions first; Desktop: actions left */}
<div className='order-1 md:order-0 w-full md:w-auto'>
<SubscriptionsActions openCreate={openCreate} t={t} />
<SubscriptionsActions
openCreate={openCreate}
t={t}
disabled={!complianceConfirmed}
/>
</div>
<Banner
type='info'
@ -94,7 +116,21 @@ const SubscriptionsPage = () => {
})}
t={t}
>
<SubscriptionsTable {...subscriptionsData} enableEpay={enableEpay} />
{!complianceConfirmed && (
<Banner
type='warning'
description={t(
'订阅套餐创建和变更已锁定,管理员需先在支付设置中确认合规声明。',
)}
closeIcon={null}
className='!rounded-lg mb-3'
/>
)}
<SubscriptionsTable
{...subscriptionsData}
enableEpay={enableEpay}
complianceConfirmed={complianceConfirmed}
/>
</CardPro>
</>
);

View File

@ -38,6 +38,7 @@ const InvitationCard = ({
setOpenTransfer,
affLink,
handleAffLinkClick,
complianceConfirmed = true,
}) => {
return (
<Card className='!rounded-2xl shadow-sm border-0'>
@ -81,6 +82,7 @@ const InvitationCard = ({
theme='solid'
size='small'
disabled={
!complianceConfirmed ||
!userState?.user?.aff_quota ||
userState?.user?.aff_quota <= 0
}
@ -91,6 +93,16 @@ const InvitationCard = ({
{t('划转到余额')}
</Button>
</div>
{!complianceConfirmed && (
<Text
style={{
color: 'rgba(255,255,255,0.8)',
fontSize: 12,
}}
>
{t('邀请奖励划转已禁用,管理员需先确认合规声明。')}
</Text>
)}
{/* 统计数据 */}
<div className='grid grid-cols-3 gap-6 mt-4'>

View File

@ -96,6 +96,7 @@ const RechargeCard = ({
activeSubscriptions = [],
allSubscriptions = [],
reloadSubscriptionSelf,
enableRedemption = true,
}) => {
const onlineFormApiRef = useRef(null);
const redeemFormApiRef = useRef(null);
@ -566,57 +567,66 @@ const RechargeCard = ({
</Card>
{/* 兑换码充值 */}
<Card
className='!rounded-xl w-full'
title={
<Text type='tertiary' strong>
{t('兑换码充值')}
</Text>
}
>
<Form
getFormApi={(api) => (redeemFormApiRef.current = api)}
initValues={{ redemptionCode: redemptionCode }}
{enableRedemption ? (
<Card
className='!rounded-xl w-full'
title={
<Text type='tertiary' strong>
{t('兑换码充值')}
</Text>
}
>
<Form.Input
field='redemptionCode'
noLabel={true}
placeholder={t('请输入兑换码')}
value={redemptionCode}
onChange={(value) => setRedemptionCode(value)}
prefix={<IconGift />}
suffix={
<div className='flex items-center gap-2'>
<Button
type='primary'
theme='solid'
onClick={topUp}
loading={isSubmitting}
>
{t('兑换额度')}
</Button>
</div>
}
showClear
style={{ width: '100%' }}
extraText={
topUpLink && (
<Text type='tertiary'>
{t('在找兑换码?')}
<Text
type='secondary'
underline
className='cursor-pointer'
onClick={openTopUpLink}
<Form
getFormApi={(api) => (redeemFormApiRef.current = api)}
initValues={{ redemptionCode: redemptionCode }}
>
<Form.Input
field='redemptionCode'
noLabel={true}
placeholder={t('请输入兑换码')}
value={redemptionCode}
onChange={(value) => setRedemptionCode(value)}
prefix={<IconGift />}
suffix={
<div className='flex items-center gap-2'>
<Button
type='primary'
theme='solid'
onClick={topUp}
loading={isSubmitting}
>
{t('购买兑换码')}
{t('兑换额度')}
</Button>
</div>
}
showClear
style={{ width: '100%' }}
extraText={
topUpLink && (
<Text type='tertiary'>
{t('在找兑换码?')}
<Text
type='secondary'
underline
className='cursor-pointer'
onClick={openTopUpLink}
>
{t('购买兑换码')}
</Text>
</Text>
</Text>
)
}
/>
</Form>
</Card>
)
}
/>
</Form>
</Card>
) : (
<Banner
type='warning'
description={t('兑换码功能已禁用,管理员需先确认合规声明。')}
closeIcon={null}
className='!rounded-xl'
/>
)}
</Space>
);

View File

@ -110,6 +110,8 @@ const TopUp = () => {
const [topupInfo, setTopupInfo] = useState({
amount_options: [],
discount: {},
enable_redemption: true,
payment_compliance_confirmed: true,
});
const confirmPayMethods = [
@ -645,7 +647,7 @@ const TopUp = () => {
? data.waffo_min_topup
: enableWaffoPancakeTopUp
? data.waffo_pancake_min_topup
: 1;
: 1;
setEnableOnlineTopUp(enableOnlineTopUp);
setEnableStripeTopUp(enableStripeTopUp);
setEnableCreemTopUp(enableCreemTopUp);
@ -657,6 +659,14 @@ const TopUp = () => {
setMinTopUp(minTopUpValue);
setTopUpCount(minTopUpValue);
setTopUpLink(data.topup_link || '');
setTopupInfo((prev) => ({
...prev,
enable_redemption: data.enable_redemption !== false,
payment_compliance_confirmed:
data.payment_compliance_confirmed !== false,
payment_compliance_terms_version:
data.payment_compliance_terms_version || '',
}));
// Creem
try {
@ -984,6 +994,7 @@ const TopUp = () => {
activeSubscriptions={activeSubscriptions}
allSubscriptions={allSubscriptions}
reloadSubscriptionSelf={getSubscriptionSelf}
enableRedemption={topupInfo.enable_redemption !== false}
/>
<InvitationCard
t={t}
@ -992,6 +1003,7 @@ const TopUp = () => {
setOpenTransfer={setOpenTransfer}
affLink={affLink}
handleAffLinkClick={handleAffLinkClick}
complianceConfirmed={topupInfo.payment_compliance_confirmed !== false}
/>
</div>
</div>

View File

@ -3794,6 +3794,36 @@
"见上方动态计费详情": "See dynamic pricing details above",
"含时间条件": "Time rules",
"含请求条件": "Request rules",
"(当前仅支持易支付接口,默认使用上方服务器地址作为回调地址!)": "(Currently only supports Epay interface, the default callback address is the server address above!)"
"(当前仅支持易支付接口,默认使用上方服务器地址作为回调地址!)": "(Currently only supports Epay interface, the default callback address is the server address above!)",
"你已合法取得所接入模型 API、账号、密钥和额度的授权": "You have legally obtained authorization for the connected model APIs, accounts, keys, and quotas.",
"你承诺仅在已取得上游服务商、模型服务提供方或相关权利方合法授权的范围内使用其 API、账号、密钥、额度及服务能力不进行未经授权的转售、倒卖、分销或其他违规商业化使用。": "You commit to using upstream APIs, accounts, keys, quotas, and service capabilities only within the scope of lawful authorization obtained from upstream service providers, model service providers, or relevant rights holders, and will not conduct unauthorized resale, trafficking, distribution, or other non-compliant commercialization.",
"如向中华人民共和国境内公众提供生成式人工智能服务,你将依法履行备案登记、安全评估、内容安全、投诉举报、生成合成内容标识、日志留存、个人信息保护等合规义务;": "If you provide generative AI services to the public in mainland China, you will fulfill legal compliance obligations.",
"你承诺不会利用本系统实施、协助实施或变相实施违反适用法律法规、监管要求、平台规则、社会公共利益或第三方合法权益的行为。": "You commit not to use this system to implement, assist with, or indirectly implement acts that violate applicable laws and regulations, regulatory requirements, platform rules, public interests, or the lawful rights and interests of third parties.",
"你理解并自行承担部署、运营和收费行为产生的法律责任。": "You understand and independently bear legal responsibility arising from deployment, operation, and charging behavior.",
"你理解本合规提醒仅用于风险提示,不构成法律意见、合规审查结论或对你使用本系统行为合法性的保证;你应根据实际业务场景自行咨询专业法律或合规顾问。": "You understand this compliance reminder is only for risk notice and does not constitute legal advice, a compliance review conclusion, or a guarantee of the legality of your use of this system; you should consult professional legal or compliance advisors based on your actual business scenario.",
"我已阅读并理解上述合规提醒,知悉相关法律风险,并确认自行承担部署、运营和收费行为产生的法律责任": "I have read and understood the above compliance reminder, acknowledge the related legal risks, and confirm that I bear legal responsibility arising from deployment, operation, and charging behavior.",
"需要确认合规声明": "Compliance confirmation required",
"确认前,支付、兑换码、订阅计划和邀请返利功能将保持锁定。": "Payment, redemption codes, subscription plans, and invitation rewards remain locked until confirmation.",
"确认合规声明": "Confirm compliance terms",
"合规声明已确认": "Compliance confirmed",
"确认时间:{{time}},确认用户:#{{userId}}": "Confirmed at {{time}} by user #{{userId}}",
"该操作将启用支付、兑换码、订阅计划和邀请返利相关功能。请仔细阅读并确认以下声明。": "This confirmation unlocks payment, redemption code, subscription plan, and invitation reward features. Please read carefully.",
"请输入以下文字以确认:": "Please type the following text to confirm:",
"请输入确认文案": "Type the confirmation text here",
"输入内容与要求文案不一致": "The entered text does not match the required text.",
"确认并启用": "Confirm and enable",
"合规声明确认成功": "Compliance confirmed successfully",
"确认失败": "Confirmation failed",
"兑换码功能已禁用,管理员需先确认合规声明。": "Redemption codes are disabled until the administrator confirms compliance terms.",
"订阅套餐创建和变更已锁定,管理员需先在支付设置中确认合规声明。": "Subscription plan creation and changes are locked until compliance terms are confirmed in payment settings.",
"邀请奖励划转已禁用,管理员需先确认合规声明。": "Invitation reward transfer is disabled until compliance terms are confirmed.",
"设置非零邀请奖励额度前,需要先在支付设置中确认合规声明。": "Non-zero invitation rewards require compliance confirmation in payment settings.",
"非零值需先确认合规声明": "Non-zero values require compliance confirmation first",
"我已阅读并理解上述合规提醒": "I have read and understood the above compliance reminder",
"知悉相关法律风险": "acknowledge the related legal risks",
"并确认自行承担部署": "confirm that I bear legal responsibility arising from deployment",
"运营和收费行为产生的法律责任": "operation and charging behavior",
"": ", ",
"、": ", "
}
}

View File

@ -3649,6 +3649,36 @@
"默认用户消息": "Bonjour",
"默认补全倍率": "Taux de complétion par défaut",
"阶梯计费(表达式解析失败)": "Facturation par paliers (échec de l'analyse de l'expression)",
"阶梯计费(未匹配到对应阶梯)": "Facturation par paliers (aucun palier correspondant)"
"阶梯计费(未匹配到对应阶梯)": "Facturation par paliers (aucun palier correspondant)",
"你已合法取得所接入模型 API、账号、密钥和额度的授权": "You have legally obtained authorization for the connected model APIs, accounts, keys, and quotas.",
"你承诺仅在已取得上游服务商、模型服务提供方或相关权利方合法授权的范围内使用其 API、账号、密钥、额度及服务能力不进行未经授权的转售、倒卖、分销或其他违规商业化使用。": "You commit to using upstream APIs, accounts, keys, quotas, and service capabilities only within the scope of lawful authorization obtained from upstream service providers, model service providers, or relevant rights holders, and will not conduct unauthorized resale, trafficking, distribution, or other non-compliant commercialization.",
"如向中华人民共和国境内公众提供生成式人工智能服务,你将依法履行备案登记、安全评估、内容安全、投诉举报、生成合成内容标识、日志留存、个人信息保护等合规义务;": "If you provide generative AI services to the public in mainland China, you will fulfill legal compliance obligations.",
"你承诺不会利用本系统实施、协助实施或变相实施违反适用法律法规、监管要求、平台规则、社会公共利益或第三方合法权益的行为。": "You commit not to use this system to implement, assist with, or indirectly implement acts that violate applicable laws and regulations, regulatory requirements, platform rules, public interests, or the lawful rights and interests of third parties.",
"你理解并自行承担部署、运营和收费行为产生的法律责任。": "You understand and independently bear legal responsibility arising from deployment, operation, and charging behavior.",
"你理解本合规提醒仅用于风险提示,不构成法律意见、合规审查结论或对你使用本系统行为合法性的保证;你应根据实际业务场景自行咨询专业法律或合规顾问。": "You understand this compliance reminder is only for risk notice and does not constitute legal advice, a compliance review conclusion, or a guarantee of the legality of your use of this system; you should consult professional legal or compliance advisors based on your actual business scenario.",
"我已阅读并理解上述合规提醒,知悉相关法律风险,并确认自行承担部署、运营和收费行为产生的法律责任": "I have read and understood the above compliance reminder, acknowledge the related legal risks, and confirm that I bear legal responsibility arising from deployment, operation, and charging behavior.",
"需要确认合规声明": "Compliance confirmation required",
"确认前,支付、兑换码、订阅计划和邀请返利功能将保持锁定。": "Payment, redemption codes, subscription plans, and invitation rewards remain locked until confirmation.",
"确认合规声明": "Confirm compliance terms",
"合规声明已确认": "Compliance confirmed",
"确认时间:{{time}},确认用户:#{{userId}}": "Confirmed at {{time}} by user #{{userId}}",
"该操作将启用支付、兑换码、订阅计划和邀请返利相关功能。请仔细阅读并确认以下声明。": "This confirmation unlocks payment, redemption code, subscription plan, and invitation reward features. Please read carefully.",
"请输入以下文字以确认:": "Please type the following text to confirm:",
"请输入确认文案": "Type the confirmation text here",
"输入内容与要求文案不一致": "The entered text does not match the required text.",
"确认并启用": "Confirm and enable",
"合规声明确认成功": "Compliance confirmed successfully",
"确认失败": "Confirmation failed",
"兑换码功能已禁用,管理员需先确认合规声明。": "Redemption codes are disabled until the administrator confirms compliance terms.",
"订阅套餐创建和变更已锁定,管理员需先在支付设置中确认合规声明。": "Subscription plan creation and changes are locked until compliance terms are confirmed in payment settings.",
"邀请奖励划转已禁用,管理员需先确认合规声明。": "Invitation reward transfer is disabled until compliance terms are confirmed.",
"设置非零邀请奖励额度前,需要先在支付设置中确认合规声明。": "Non-zero invitation rewards require compliance confirmation in payment settings.",
"非零值需先确认合规声明": "Non-zero values require compliance confirmation first",
"我已阅读并理解上述合规提醒": "I have read and understood the above compliance reminder",
"知悉相关法律风险": "acknowledge the related legal risks",
"并确认自行承担部署": "confirm that I bear legal responsibility arising from deployment",
"运营和收费行为产生的法律责任": "operation and charging behavior",
"": ", ",
"、": ", "
}
}

View File

@ -3618,6 +3618,36 @@
"默认用户消息": "こんにちは",
"默认补全倍率": "デフォルト補完倍率",
"阶梯计费(表达式解析失败)": "段階課金(式の解析に失敗)",
"阶梯计费(未匹配到对应阶梯)": "段階課金(一致する階層なし)"
"阶梯计费(未匹配到对应阶梯)": "段階課金(一致する階層なし)",
"你已合法取得所接入模型 API、账号、密钥和额度的授权": "You have legally obtained authorization for the connected model APIs, accounts, keys, and quotas.",
"你承诺仅在已取得上游服务商、模型服务提供方或相关权利方合法授权的范围内使用其 API、账号、密钥、额度及服务能力不进行未经授权的转售、倒卖、分销或其他违规商业化使用。": "You commit to using upstream APIs, accounts, keys, quotas, and service capabilities only within the scope of lawful authorization obtained from upstream service providers, model service providers, or relevant rights holders, and will not conduct unauthorized resale, trafficking, distribution, or other non-compliant commercialization.",
"如向中华人民共和国境内公众提供生成式人工智能服务,你将依法履行备案登记、安全评估、内容安全、投诉举报、生成合成内容标识、日志留存、个人信息保护等合规义务;": "If you provide generative AI services to the public in mainland China, you will fulfill legal compliance obligations.",
"你承诺不会利用本系统实施、协助实施或变相实施违反适用法律法规、监管要求、平台规则、社会公共利益或第三方合法权益的行为。": "You commit not to use this system to implement, assist with, or indirectly implement acts that violate applicable laws and regulations, regulatory requirements, platform rules, public interests, or the lawful rights and interests of third parties.",
"你理解并自行承担部署、运营和收费行为产生的法律责任。": "You understand and independently bear legal responsibility arising from deployment, operation, and charging behavior.",
"你理解本合规提醒仅用于风险提示,不构成法律意见、合规审查结论或对你使用本系统行为合法性的保证;你应根据实际业务场景自行咨询专业法律或合规顾问。": "You understand this compliance reminder is only for risk notice and does not constitute legal advice, a compliance review conclusion, or a guarantee of the legality of your use of this system; you should consult professional legal or compliance advisors based on your actual business scenario.",
"我已阅读并理解上述合规提醒,知悉相关法律风险,并确认自行承担部署、运营和收费行为产生的法律责任": "I have read and understood the above compliance reminder, acknowledge the related legal risks, and confirm that I bear legal responsibility arising from deployment, operation, and charging behavior.",
"需要确认合规声明": "Compliance confirmation required",
"确认前,支付、兑换码、订阅计划和邀请返利功能将保持锁定。": "Payment, redemption codes, subscription plans, and invitation rewards remain locked until confirmation.",
"确认合规声明": "Confirm compliance terms",
"合规声明已确认": "Compliance confirmed",
"确认时间:{{time}},确认用户:#{{userId}}": "Confirmed at {{time}} by user #{{userId}}",
"该操作将启用支付、兑换码、订阅计划和邀请返利相关功能。请仔细阅读并确认以下声明。": "This confirmation unlocks payment, redemption code, subscription plan, and invitation reward features. Please read carefully.",
"请输入以下文字以确认:": "Please type the following text to confirm:",
"请输入确认文案": "Type the confirmation text here",
"输入内容与要求文案不一致": "The entered text does not match the required text.",
"确认并启用": "Confirm and enable",
"合规声明确认成功": "Compliance confirmed successfully",
"确认失败": "Confirmation failed",
"兑换码功能已禁用,管理员需先确认合规声明。": "Redemption codes are disabled until the administrator confirms compliance terms.",
"订阅套餐创建和变更已锁定,管理员需先在支付设置中确认合规声明。": "Subscription plan creation and changes are locked until compliance terms are confirmed in payment settings.",
"邀请奖励划转已禁用,管理员需先确认合规声明。": "Invitation reward transfer is disabled until compliance terms are confirmed.",
"设置非零邀请奖励额度前,需要先在支付设置中确认合规声明。": "Non-zero invitation rewards require compliance confirmation in payment settings.",
"非零值需先确认合规声明": "Non-zero values require compliance confirmation first",
"我已阅读并理解上述合规提醒": "I have read and understood the above compliance reminder",
"知悉相关法律风险": "acknowledge the related legal risks",
"并确认自行承担部署": "confirm that I bear legal responsibility arising from deployment",
"运营和收费行为产生的法律责任": "operation and charging behavior",
"": ", ",
"、": ", "
}
}

View File

@ -3669,6 +3669,36 @@
"默认用户消息": "Здравствуйте",
"默认补全倍率": "Коэффициент завершения по умолчанию",
"阶梯计费(表达式解析失败)": "Многоуровневая тарификация (ошибка разбора выражения)",
"阶梯计费(未匹配到对应阶梯)": "Многоуровневая тарификация (подходящий уровень не найден)"
"阶梯计费(未匹配到对应阶梯)": "Многоуровневая тарификация (подходящий уровень не найден)",
"你已合法取得所接入模型 API、账号、密钥和额度的授权": "You have legally obtained authorization for the connected model APIs, accounts, keys, and quotas.",
"你承诺仅在已取得上游服务商、模型服务提供方或相关权利方合法授权的范围内使用其 API、账号、密钥、额度及服务能力不进行未经授权的转售、倒卖、分销或其他违规商业化使用。": "You commit to using upstream APIs, accounts, keys, quotas, and service capabilities only within the scope of lawful authorization obtained from upstream service providers, model service providers, or relevant rights holders, and will not conduct unauthorized resale, trafficking, distribution, or other non-compliant commercialization.",
"如向中华人民共和国境内公众提供生成式人工智能服务,你将依法履行备案登记、安全评估、内容安全、投诉举报、生成合成内容标识、日志留存、个人信息保护等合规义务;": "If you provide generative AI services to the public in mainland China, you will fulfill legal compliance obligations.",
"你承诺不会利用本系统实施、协助实施或变相实施违反适用法律法规、监管要求、平台规则、社会公共利益或第三方合法权益的行为。": "You commit not to use this system to implement, assist with, or indirectly implement acts that violate applicable laws and regulations, regulatory requirements, platform rules, public interests, or the lawful rights and interests of third parties.",
"你理解并自行承担部署、运营和收费行为产生的法律责任。": "You understand and independently bear legal responsibility arising from deployment, operation, and charging behavior.",
"你理解本合规提醒仅用于风险提示,不构成法律意见、合规审查结论或对你使用本系统行为合法性的保证;你应根据实际业务场景自行咨询专业法律或合规顾问。": "You understand this compliance reminder is only for risk notice and does not constitute legal advice, a compliance review conclusion, or a guarantee of the legality of your use of this system; you should consult professional legal or compliance advisors based on your actual business scenario.",
"我已阅读并理解上述合规提醒,知悉相关法律风险,并确认自行承担部署、运营和收费行为产生的法律责任": "I have read and understood the above compliance reminder, acknowledge the related legal risks, and confirm that I bear legal responsibility arising from deployment, operation, and charging behavior.",
"需要确认合规声明": "Compliance confirmation required",
"确认前,支付、兑换码、订阅计划和邀请返利功能将保持锁定。": "Payment, redemption codes, subscription plans, and invitation rewards remain locked until confirmation.",
"确认合规声明": "Confirm compliance terms",
"合规声明已确认": "Compliance confirmed",
"确认时间:{{time}},确认用户:#{{userId}}": "Confirmed at {{time}} by user #{{userId}}",
"该操作将启用支付、兑换码、订阅计划和邀请返利相关功能。请仔细阅读并确认以下声明。": "This confirmation unlocks payment, redemption code, subscription plan, and invitation reward features. Please read carefully.",
"请输入以下文字以确认:": "Please type the following text to confirm:",
"请输入确认文案": "Type the confirmation text here",
"输入内容与要求文案不一致": "The entered text does not match the required text.",
"确认并启用": "Confirm and enable",
"合规声明确认成功": "Compliance confirmed successfully",
"确认失败": "Confirmation failed",
"兑换码功能已禁用,管理员需先确认合规声明。": "Redemption codes are disabled until the administrator confirms compliance terms.",
"订阅套餐创建和变更已锁定,管理员需先在支付设置中确认合规声明。": "Subscription plan creation and changes are locked until compliance terms are confirmed in payment settings.",
"邀请奖励划转已禁用,管理员需先确认合规声明。": "Invitation reward transfer is disabled until compliance terms are confirmed.",
"设置非零邀请奖励额度前,需要先在支付设置中确认合规声明。": "Non-zero invitation rewards require compliance confirmation in payment settings.",
"非零值需先确认合规声明": "Non-zero values require compliance confirmation first",
"我已阅读并理解上述合规提醒": "I have read and understood the above compliance reminder",
"知悉相关法律风险": "acknowledge the related legal risks",
"并确认自行承担部署": "confirm that I bear legal responsibility arising from deployment",
"运营和收费行为产生的法律责任": "operation and charging behavior",
"": ", ",
"、": ", "
}
}

View File

@ -4183,6 +4183,36 @@
"默认用户消息": "Xin chào",
"默认补全倍率": "Tỷ lệ hoàn thành mặc định",
"阶梯计费(表达式解析失败)": "Thanh toán theo bậc (không phân tích được biểu thức)",
"阶梯计费(未匹配到对应阶梯)": "Thanh toán theo bậc (không tìm thấy bậc phù hợp)"
"阶梯计费(未匹配到对应阶梯)": "Thanh toán theo bậc (không tìm thấy bậc phù hợp)",
"你已合法取得所接入模型 API、账号、密钥和额度的授权": "You have legally obtained authorization for the connected model APIs, accounts, keys, and quotas.",
"你承诺仅在已取得上游服务商、模型服务提供方或相关权利方合法授权的范围内使用其 API、账号、密钥、额度及服务能力不进行未经授权的转售、倒卖、分销或其他违规商业化使用。": "You commit to using upstream APIs, accounts, keys, quotas, and service capabilities only within the scope of lawful authorization obtained from upstream service providers, model service providers, or relevant rights holders, and will not conduct unauthorized resale, trafficking, distribution, or other non-compliant commercialization.",
"如向中华人民共和国境内公众提供生成式人工智能服务,你将依法履行备案登记、安全评估、内容安全、投诉举报、生成合成内容标识、日志留存、个人信息保护等合规义务;": "If you provide generative AI services to the public in mainland China, you will fulfill legal compliance obligations.",
"你承诺不会利用本系统实施、协助实施或变相实施违反适用法律法规、监管要求、平台规则、社会公共利益或第三方合法权益的行为。": "You commit not to use this system to implement, assist with, or indirectly implement acts that violate applicable laws and regulations, regulatory requirements, platform rules, public interests, or the lawful rights and interests of third parties.",
"你理解并自行承担部署、运营和收费行为产生的法律责任。": "You understand and independently bear legal responsibility arising from deployment, operation, and charging behavior.",
"你理解本合规提醒仅用于风险提示,不构成法律意见、合规审查结论或对你使用本系统行为合法性的保证;你应根据实际业务场景自行咨询专业法律或合规顾问。": "You understand this compliance reminder is only for risk notice and does not constitute legal advice, a compliance review conclusion, or a guarantee of the legality of your use of this system; you should consult professional legal or compliance advisors based on your actual business scenario.",
"我已阅读并理解上述合规提醒,知悉相关法律风险,并确认自行承担部署、运营和收费行为产生的法律责任": "I have read and understood the above compliance reminder, acknowledge the related legal risks, and confirm that I bear legal responsibility arising from deployment, operation, and charging behavior.",
"需要确认合规声明": "Compliance confirmation required",
"确认前,支付、兑换码、订阅计划和邀请返利功能将保持锁定。": "Payment, redemption codes, subscription plans, and invitation rewards remain locked until confirmation.",
"确认合规声明": "Confirm compliance terms",
"合规声明已确认": "Compliance confirmed",
"确认时间:{{time}},确认用户:#{{userId}}": "Confirmed at {{time}} by user #{{userId}}",
"该操作将启用支付、兑换码、订阅计划和邀请返利相关功能。请仔细阅读并确认以下声明。": "This confirmation unlocks payment, redemption code, subscription plan, and invitation reward features. Please read carefully.",
"请输入以下文字以确认:": "Please type the following text to confirm:",
"请输入确认文案": "Type the confirmation text here",
"输入内容与要求文案不一致": "The entered text does not match the required text.",
"确认并启用": "Confirm and enable",
"合规声明确认成功": "Compliance confirmed successfully",
"确认失败": "Confirmation failed",
"兑换码功能已禁用,管理员需先确认合规声明。": "Redemption codes are disabled until the administrator confirms compliance terms.",
"订阅套餐创建和变更已锁定,管理员需先在支付设置中确认合规声明。": "Subscription plan creation and changes are locked until compliance terms are confirmed in payment settings.",
"邀请奖励划转已禁用,管理员需先确认合规声明。": "Invitation reward transfer is disabled until compliance terms are confirmed.",
"设置非零邀请奖励额度前,需要先在支付设置中确认合规声明。": "Non-zero invitation rewards require compliance confirmation in payment settings.",
"非零值需先确认合规声明": "Non-zero values require compliance confirmation first",
"我已阅读并理解上述合规提醒": "I have read and understood the above compliance reminder",
"知悉相关法律风险": "acknowledge the related legal risks",
"并确认自行承担部署": "confirm that I bear legal responsibility arising from deployment",
"运营和收费行为产生的法律责任": "operation and charging behavior",
"": ", ",
"、": ", "
}
}

View File

@ -3778,6 +3778,36 @@
"缓存创建-1h": "缓存创建-1h",
"见上方动态计费详情": "见上方动态计费详情",
"含时间条件": "含时间条件",
"含请求条件": "含请求条件"
"含请求条件": "含请求条件",
"你已合法取得所接入模型 API、账号、密钥和额度的授权": "你已合法取得所接入模型 API、账号、密钥和额度的授权",
"你承诺仅在已取得上游服务商、模型服务提供方或相关权利方合法授权的范围内使用其 API、账号、密钥、额度及服务能力不进行未经授权的转售、倒卖、分销或其他违规商业化使用。": "你承诺仅在已取得上游服务商、模型服务提供方或相关权利方合法授权的范围内使用其 API、账号、密钥、额度及服务能力不进行未经授权的转售、倒卖、分销或其他违规商业化使用。",
"如向中华人民共和国境内公众提供生成式人工智能服务,你将依法履行备案登记、安全评估、内容安全、投诉举报、生成合成内容标识、日志留存、个人信息保护等合规义务;": "如向中华人民共和国境内公众提供生成式人工智能服务,你将依法履行备案登记、安全评估、内容安全、投诉举报、生成合成内容标识、日志留存、个人信息保护等合规义务;",
"你承诺不会利用本系统实施、协助实施或变相实施违反适用法律法规、监管要求、平台规则、社会公共利益或第三方合法权益的行为。": "你承诺不会利用本系统实施、协助实施或变相实施违反适用法律法规、监管要求、平台规则、社会公共利益或第三方合法权益的行为。",
"你理解并自行承担部署、运营和收费行为产生的法律责任。": "你理解并自行承担部署、运营和收费行为产生的法律责任。",
"你理解本合规提醒仅用于风险提示,不构成法律意见、合规审查结论或对你使用本系统行为合法性的保证;你应根据实际业务场景自行咨询专业法律或合规顾问。": "你理解本合规提醒仅用于风险提示,不构成法律意见、合规审查结论或对你使用本系统行为合法性的保证;你应根据实际业务场景自行咨询专业法律或合规顾问。",
"我已阅读并理解上述合规提醒,知悉相关法律风险,并确认自行承担部署、运营和收费行为产生的法律责任": "我已阅读并理解上述合规提醒,知悉相关法律风险,并确认自行承担部署、运营和收费行为产生的法律责任",
"需要确认合规声明": "需要确认合规声明",
"确认前,支付、兑换码、订阅计划和邀请返利功能将保持锁定。": "确认前,支付、兑换码、订阅计划和邀请返利功能将保持锁定。",
"确认合规声明": "确认合规声明",
"合规声明已确认": "合规声明已确认",
"确认时间:{{time}},确认用户:#{{userId}}": "确认时间:{{time}},确认用户:#{{userId}}",
"该操作将启用支付、兑换码、订阅计划和邀请返利相关功能。请仔细阅读并确认以下声明。": "该操作将启用支付、兑换码、订阅计划和邀请返利相关功能。请仔细阅读并确认以下声明。",
"请输入以下文字以确认:": "请输入以下文字以确认:",
"请输入确认文案": "请输入确认文案",
"输入内容与要求文案不一致": "输入内容与要求文案不一致",
"确认并启用": "确认并启用",
"合规声明确认成功": "合规声明确认成功",
"确认失败": "确认失败",
"兑换码功能已禁用,管理员需先确认合规声明。": "兑换码功能已禁用,管理员需先确认合规声明。",
"订阅套餐创建和变更已锁定,管理员需先在支付设置中确认合规声明。": "订阅套餐创建和变更已锁定,管理员需先在支付设置中确认合规声明。",
"邀请奖励划转已禁用,管理员需先确认合规声明。": "邀请奖励划转已禁用,管理员需先确认合规声明。",
"设置非零邀请奖励额度前,需要先在支付设置中确认合规声明。": "设置非零邀请奖励额度前,需要先在支付设置中确认合规声明。",
"非零值需先确认合规声明": "非零值需先确认合规声明",
"我已阅读并理解上述合规提醒": "我已阅读并理解上述合规提醒",
"知悉相关法律风险": "知悉相关法律风险",
"并确认自行承担部署": "并确认自行承担部署",
"运营和收费行为产生的法律责任": "运营和收费行为产生的法律责任",
"": "",
"、": "、"
}
}

View File

@ -3642,6 +3642,36 @@
"默认用户消息": "你好",
"默认补全倍率": "預設補全倍率",
"阶梯计费(表达式解析失败)": "階梯計費(表達式解析失敗)",
"阶梯计费(未匹配到对应阶梯)": "階梯計費(未匹配到對應階梯)"
"阶梯计费(未匹配到对应阶梯)": "階梯計費(未匹配到對應階梯)",
"你已合法取得所接入模型 API、账号、密钥和额度的授权": "你已合法取得所接入模型 API、账号、密钥和额度的授权",
"你承诺仅在已取得上游服务商、模型服务提供方或相关权利方合法授权的范围内使用其 API、账号、密钥、额度及服务能力不进行未经授权的转售、倒卖、分销或其他违规商业化使用。": "你承諾僅在已取得上游服務商、模型服務提供方或相關權利方合法授權的範圍內使用其 API、帳號、金鑰、額度及服務能力不進行未經授權的轉售、倒賣、分銷或其他違規商業化使用。",
"如向中华人民共和国境内公众提供生成式人工智能服务,你将依法履行备案登记、安全评估、内容安全、投诉举报、生成合成内容标识、日志留存、个人信息保护等合规义务;": "如向中华人民共和国境内公众提供生成式人工智能服务,你将依法履行备案登记、安全评估、内容安全、投诉举报、生成合成内容标识、日志留存、个人信息保护等合规义务;",
"你承诺不会利用本系统实施、协助实施或变相实施违反适用法律法规、监管要求、平台规则、社会公共利益或第三方合法权益的行为。": "你承諾不會利用本系統實施、協助實施或變相實施違反適用法律法規、監管要求、平台規則、社會公共利益或第三方合法權益的行為。",
"你理解并自行承担部署、运营和收费行为产生的法律责任。": "你理解并自行承担部署、运营和收费行为产生的法律责任。",
"你理解本合规提醒仅用于风险提示,不构成法律意见、合规审查结论或对你使用本系统行为合法性的保证;你应根据实际业务场景自行咨询专业法律或合规顾问。": "你理解本合規提醒僅用於風險提示,不構成法律意見、合規審查結論或對你使用本系統行為合法性的保證;你應根據實際業務場景自行諮詢專業法律或合規顧問。",
"我已阅读并理解上述合规提醒,知悉相关法律风险,并确认自行承担部署、运营和收费行为产生的法律责任": "我已閱讀並理解上述合規提醒,知悉相關法律風險,並確認自行承擔部署、營運和收費行為產生的法律責任",
"需要确认合规声明": "需要确认合规声明",
"确认前,支付、兑换码、订阅计划和邀请返利功能将保持锁定。": "确认前,支付、兑换码、订阅计划和邀请返利功能将保持锁定。",
"确认合规声明": "确认合规声明",
"合规声明已确认": "合规声明已确认",
"确认时间:{{time}},确认用户:#{{userId}}": "确认时间:{{time}},确认用户:#{{userId}}",
"该操作将启用支付、兑换码、订阅计划和邀请返利相关功能。请仔细阅读并确认以下声明。": "该操作将启用支付、兑换码、订阅计划和邀请返利相关功能。请仔细阅读并确认以下声明。",
"请输入以下文字以确认:": "请输入以下文字以确认:",
"请输入确认文案": "请输入确认文案",
"输入内容与要求文案不一致": "输入内容与要求文案不一致",
"确认并启用": "确认并启用",
"合规声明确认成功": "合规声明确认成功",
"确认失败": "确认失败",
"兑换码功能已禁用,管理员需先确认合规声明。": "兑换码功能已禁用,管理员需先确认合规声明。",
"订阅套餐创建和变更已锁定,管理员需先在支付设置中确认合规声明。": "订阅套餐创建和变更已锁定,管理员需先在支付设置中确认合规声明。",
"邀请奖励划转已禁用,管理员需先确认合规声明。": "邀请奖励划转已禁用,管理员需先确认合规声明。",
"设置非零邀请奖励额度前,需要先在支付设置中确认合规声明。": "设置非零邀请奖励额度前,需要先在支付设置中确认合规声明。",
"非零值需先确认合规声明": "非零值需先确认合规声明",
"我已阅读并理解上述合规提醒": "我已閱讀並理解上述合規提醒",
"知悉相关法律风险": "知悉相關法律風險",
"并确认自行承担部署": "並確認自行承擔部署",
"运营和收费行为产生的法律责任": "營運和收費行為產生的法律責任",
"": "",
"、": "、"
}
}

View File

@ -2561,7 +2561,6 @@
"默认补全倍率": "默认补全倍率",
"每日签到": "每日签到",
"今日已签到,累计签到": "今日已签到,累计签到",
"天": "天",
"每日签到可获得随机额度奖励": "每日签到可获得随机额度奖励",
"今日已签到": "今日已签到",
"立即签到": "立即签到",
@ -2595,6 +2594,36 @@
"说明:本页测试为非流式请求;若渠道仅支持流式返回,可能出现测试失败,请以实际使用为准。": "说明:本页测试为非流式请求;若渠道仅支持流式返回,可能出现测试失败,请以实际使用为准。",
"提示:端点映射仅用于模型广场展示,不会影响模型真实调用。如需配置真实调用,请前往「渠道管理」。": "提示:端点映射仅用于模型广场展示,不会影响模型真实调用。如需配置真实调用,请前往「渠道管理」。",
"阶梯计费(表达式解析失败)": "阶梯计费(表达式解析失败)",
"阶梯计费(未匹配到对应阶梯)": "阶梯计费(未匹配到对应阶梯)"
"阶梯计费(未匹配到对应阶梯)": "阶梯计费(未匹配到对应阶梯)",
"你已合法取得所接入模型 API、账号、密钥和额度的授权": "你已合法取得所接入模型 API、账号、密钥和额度的授权",
"你承诺仅在已取得上游服务商、模型服务提供方或相关权利方合法授权的范围内使用其 API、账号、密钥、额度及服务能力不进行未经授权的转售、倒卖、分销或其他违规商业化使用。": "你承诺仅在已取得上游服务商、模型服务提供方或相关权利方合法授权的范围内使用其 API、账号、密钥、额度及服务能力不进行未经授权的转售、倒卖、分销或其他违规商业化使用。",
"如向中华人民共和国境内公众提供生成式人工智能服务,你将依法履行备案登记、安全评估、内容安全、投诉举报、生成合成内容标识、日志留存、个人信息保护等合规义务;": "如向中华人民共和国境内公众提供生成式人工智能服务,你将依法履行备案登记、安全评估、内容安全、投诉举报、生成合成内容标识、日志留存、个人信息保护等合规义务;",
"你承诺不会利用本系统实施、协助实施或变相实施违反适用法律法规、监管要求、平台规则、社会公共利益或第三方合法权益的行为。": "你承诺不会利用本系统实施、协助实施或变相实施违反适用法律法规、监管要求、平台规则、社会公共利益或第三方合法权益的行为。",
"你理解并自行承担部署、运营和收费行为产生的法律责任。": "你理解并自行承担部署、运营和收费行为产生的法律责任。",
"你理解本合规提醒仅用于风险提示,不构成法律意见、合规审查结论或对你使用本系统行为合法性的保证;你应根据实际业务场景自行咨询专业法律或合规顾问。": "你理解本合规提醒仅用于风险提示,不构成法律意见、合规审查结论或对你使用本系统行为合法性的保证;你应根据实际业务场景自行咨询专业法律或合规顾问。",
"我已阅读并理解上述合规提醒,知悉相关法律风险,并确认自行承担部署、运营和收费行为产生的法律责任": "我已阅读并理解上述合规提醒,知悉相关法律风险,并确认自行承担部署、运营和收费行为产生的法律责任",
"需要确认合规声明": "需要确认合规声明",
"确认前,支付、兑换码、订阅计划和邀请返利功能将保持锁定。": "确认前,支付、兑换码、订阅计划和邀请返利功能将保持锁定。",
"确认合规声明": "确认合规声明",
"合规声明已确认": "合规声明已确认",
"确认时间:{{time}},确认用户:#{{userId}}": "确认时间:{{time}},确认用户:#{{userId}}",
"该操作将启用支付、兑换码、订阅计划和邀请返利相关功能。请仔细阅读并确认以下声明。": "该操作将启用支付、兑换码、订阅计划和邀请返利相关功能。请仔细阅读并确认以下声明。",
"请输入以下文字以确认:": "请输入以下文字以确认:",
"请输入确认文案": "请输入确认文案",
"输入内容与要求文案不一致": "输入内容与要求文案不一致",
"确认并启用": "确认并启用",
"合规声明确认成功": "合规声明确认成功",
"确认失败": "确认失败",
"兑换码功能已禁用,管理员需先确认合规声明。": "兑换码功能已禁用,管理员需先确认合规声明。",
"订阅套餐创建和变更已锁定,管理员需先在支付设置中确认合规声明。": "订阅套餐创建和变更已锁定,管理员需先在支付设置中确认合规声明。",
"邀请奖励划转已禁用,管理员需先确认合规声明。": "邀请奖励划转已禁用,管理员需先确认合规声明。",
"设置非零邀请奖励额度前,需要先在支付设置中确认合规声明。": "设置非零邀请奖励额度前,需要先在支付设置中确认合规声明。",
"非零值需先确认合规声明": "非零值需先确认合规声明",
"我已阅读并理解上述合规提醒": "我已阅读并理解上述合规提醒",
"知悉相关法律风险": "知悉相关法律风险",
"并确认自行承担部署": "并确认自行承担部署",
"运营和收费行为产生的法律责任": "运营和收费行为产生的法律责任",
"": "",
"、": "、"
}
}

View File

@ -18,7 +18,7 @@ For commercial licensing, please contact support@quantumnous.com
*/
import React, { useEffect, useState, useRef } from 'react';
import { Button, Col, Form, Row, Spin } from '@douyinfe/semi-ui';
import { Banner, Button, Col, Form, Row, Spin } from '@douyinfe/semi-ui';
import { useTranslation } from 'react-i18next';
import {
compareObjects,
@ -40,6 +40,9 @@ export default function SettingsCreditLimit(props) {
});
const refForm = useRef();
const [inputsRow, setInputsRow] = useState(inputs);
const complianceConfirmed =
props.options?.['payment_setting.compliance_confirmed'] === true ||
props.options?.['payment_setting.compliance_confirmed'] === 'true';
function onSubmit() {
const updateArray = compareObjects(inputs, inputsRow);
@ -90,6 +93,16 @@ export default function SettingsCreditLimit(props) {
return (
<>
<Spin spinning={loading}>
{!complianceConfirmed && (
<Banner
type='warning'
description={t(
'设置非零邀请奖励额度前,需要先在支付设置中确认合规声明。',
)}
closeIcon={null}
className='!rounded-lg mb-3'
/>
)}
<Form
values={inputs}
getFormApi={(formAPI) => (refForm.current = formAPI)}
@ -137,7 +150,9 @@ export default function SettingsCreditLimit(props) {
step={1}
min={0}
suffix={'Token'}
extraText={''}
extraText={
!complianceConfirmed ? t('非零值需先确认合规声明') : ''
}
placeholder={t('例如2000')}
onChange={(value) =>
setInputs({
@ -156,7 +171,9 @@ export default function SettingsCreditLimit(props) {
step={1}
min={0}
suffix={'Token'}
extraText={''}
extraText={
!complianceConfirmed ? t('非零值需先确认合规声明') : ''
}
placeholder={t('例如1000')}
onChange={(value) =>
setInputs({

View File

@ -0,0 +1,308 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { useEffect, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { cn } from '@/lib/utils'
import {
AlertDialog,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
} from '@/components/ui/alert-dialog'
import { Button } from '@/components/ui/button'
import { Checkbox } from '@/components/ui/checkbox'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
type RequiredTextPart = {
type: 'input' | 'static'
text: string
placeholder?: string
}
type NormalizedRequiredTextPart = RequiredTextPart & {
inputIndex?: number
}
type RiskAcknowledgementDialogProps = {
open: boolean
onOpenChange: (open: boolean) => void
title: string
description?: React.ReactNode
items?: string[]
checklist?: string[]
requiredText?: string
requiredTextParts?: RequiredTextPart[]
inputPrompt?: string
inputPlaceholder?: string
mismatchHint?: string
confirmText?: string
cancelText?: string
destructive?: boolean
isLoading?: boolean
onConfirm: () => void
className?: string
}
export function RiskAcknowledgementDialog({
open,
onOpenChange,
title,
description,
items = [],
checklist = [],
requiredText = '',
requiredTextParts = [],
inputPrompt,
inputPlaceholder,
mismatchHint,
confirmText,
cancelText,
destructive = true,
isLoading = false,
onConfirm,
className,
}: RiskAcknowledgementDialogProps) {
const { t } = useTranslation()
const [checkedItems, setCheckedItems] = useState<boolean[]>([])
const [typedText, setTypedText] = useState('')
const [typedTextParts, setTypedTextParts] = useState<string[]>([])
const normalizedRequiredTextParts = useMemo<
NormalizedRequiredTextPart[]
>(() => {
let inputIndex = 0
return requiredTextParts.map((part) => {
if (part.type === 'input') {
const normalizedPart = { ...part, inputIndex }
inputIndex += 1
return normalizedPart
}
return part
})
}, [requiredTextParts])
const requiredTextInputCount = useMemo(
() =>
normalizedRequiredTextParts.filter((part) => part.type === 'input')
.length,
[normalizedRequiredTextParts]
)
const hasSegmentedRequiredText = requiredTextInputCount > 0
const requiredTextToDisplay = hasSegmentedRequiredText
? normalizedRequiredTextParts.map((part) => part.text).join('')
: requiredText
useEffect(() => {
if (!open) return
setCheckedItems(Array(checklist.length).fill(false))
setTypedText('')
setTypedTextParts(Array(requiredTextInputCount).fill(''))
}, [open, checklist.length, requiredTextInputCount])
const allChecked = useMemo(() => {
if (checklist.length === 0) return true
return (
checkedItems.length === checklist.length &&
checkedItems.every((checked) => checked)
)
}, [checkedItems, checklist.length])
const typedMatched = useMemo(() => {
if (hasSegmentedRequiredText) {
return normalizedRequiredTextParts.every((part) => {
if (part.type === 'static') return true
return typedTextParts[part.inputIndex ?? 0]?.trim() === part.text.trim()
})
}
if (!requiredText) return true
return typedText.trim() === requiredText.trim()
}, [
hasSegmentedRequiredText,
normalizedRequiredTextParts,
requiredText,
typedText,
typedTextParts,
])
const hasTypedRequiredText = hasSegmentedRequiredText
? typedTextParts.some((part) => part.trim() !== '')
: typedText.length > 0
const canConfirm = allChecked && typedMatched && !isLoading
const handleChecklistChange = (index: number, checked: boolean) => {
setCheckedItems((previous) => {
const next = [...previous]
next[index] = checked
return next
})
}
const handleTextPartChange = (index: number, value: string) => {
setTypedTextParts((previous) => {
const next = [...previous]
next[index] = value
return next
})
}
return (
<AlertDialog open={open} onOpenChange={onOpenChange}>
<AlertDialogContent
className={cn(
'flex max-h-[min(88dvh,760px)] w-[calc(100vw-1.5rem)] !max-w-[44rem] grid-rows-none flex-col gap-0 overflow-hidden !p-0 sm:w-[min(44rem,calc(100vw-3rem))]',
className
)}
>
<AlertDialogHeader className='shrink-0 px-4 pt-4 pb-3 text-left sm:px-6 sm:pt-6'>
<AlertDialogTitle>{title}</AlertDialogTitle>
{description ? (
<AlertDialogDescription
render={<div />}
className='mt-1 text-left leading-5'
>
{description}
</AlertDialogDescription>
) : null}
</AlertDialogHeader>
<div className='min-h-0 flex-1 space-y-4 overflow-y-auto px-4 pb-4 sm:px-6'>
{items.length > 0 ? (
<ol className='border-border/70 bg-muted/30 text-foreground list-decimal space-y-2 rounded-lg border px-4 py-3 pl-8 text-sm leading-6 sm:px-5 sm:py-4 sm:pl-9'>
{items.map((item) => (
<li key={item}>{item}</li>
))}
</ol>
) : null}
{checklist.length > 0 ? (
<div className='border-border/70 bg-muted/30 space-y-3 rounded-lg border p-3 sm:p-4'>
{checklist.map((item, index) => {
const id = `risk-acknowledgement-${index}`
return (
<div key={item} className='flex items-start gap-3'>
<Checkbox
id={id}
checked={checkedItems[index] ?? false}
onCheckedChange={(checked) =>
handleChecklistChange(index, checked === true)
}
className='mt-0.5'
/>
<Label
htmlFor={id}
className='text-muted-foreground text-sm leading-5 font-normal'
>
{item}
</Label>
</div>
)
})}
</div>
) : null}
{requiredTextToDisplay ? (
<div className='border-destructive/30 bg-destructive/5 space-y-3 rounded-lg border p-3 sm:p-4'>
<Label className='text-sm font-medium'>
{inputPrompt ?? t('Please type the following text to confirm:')}
</Label>
<div className='bg-background border-border rounded-md border px-3 py-2 font-mono text-sm break-all'>
{requiredTextToDisplay}
</div>
{hasSegmentedRequiredText ? (
<div className='flex flex-wrap items-center gap-2'>
{normalizedRequiredTextParts.map((part, index) =>
part.type === 'static' ? (
<span
key={`static-${index}`}
className='text-muted-foreground bg-background/70 border-border rounded-md border px-2 py-1.5 font-mono text-sm select-none'
>
{part.text}
</span>
) : (
<Input
key={`input-${index}`}
value={typedTextParts[part.inputIndex ?? 0] ?? ''}
onChange={(event) =>
handleTextPartChange(
part.inputIndex ?? 0,
event.target.value
)
}
placeholder={
part.placeholder ??
part.text ??
inputPlaceholder ??
t('Type the confirmation text here')
}
autoFocus={open && part.inputIndex === 0}
onCopy={(event) => event.preventDefault()}
onCut={(event) => event.preventDefault()}
onPaste={(event) => event.preventDefault()}
onDrop={(event) => event.preventDefault()}
aria-invalid={hasTypedRequiredText && !typedMatched}
className='w-full font-mono sm:w-64'
/>
)
)}
</div>
) : (
<Input
value={typedText}
onChange={(event) => setTypedText(event.target.value)}
placeholder={
inputPlaceholder ?? t('Type the confirmation text here')
}
autoFocus={open}
onCopy={(event) => event.preventDefault()}
onCut={(event) => event.preventDefault()}
onPaste={(event) => event.preventDefault()}
onDrop={(event) => event.preventDefault()}
aria-invalid={hasTypedRequiredText && !typedMatched}
/>
)}
{hasTypedRequiredText && !typedMatched ? (
<p className='text-destructive text-xs'>
{mismatchHint ??
t('The entered text does not match the required text.')}
</p>
) : null}
</div>
) : null}
</div>
<AlertDialogFooter className='mx-0 mb-0 shrink-0 rounded-b-xl border-t p-3 sm:p-4'>
<AlertDialogCancel disabled={isLoading}>
{cancelText ?? t('Cancel')}
</AlertDialogCancel>
<Button
variant={destructive ? 'destructive' : 'default'}
disabled={!canConfirm}
onClick={onConfirm}
>
{confirmText ?? t('Confirm')}
</Button>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
)
}

View File

@ -35,7 +35,7 @@ interface DataTableRowActionsProps {
export function DataTableRowActions({ row }: DataTableRowActionsProps) {
const { t } = useTranslation()
const { setOpen, setCurrentRow } = useSubscriptions()
const { setOpen, setCurrentRow, complianceConfirmed } = useSubscriptions()
return (
<DropdownMenu>
@ -46,6 +46,7 @@ export function DataTableRowActions({ row }: DataTableRowActionsProps) {
</DropdownMenuTrigger>
<DropdownMenuContent align='end'>
<DropdownMenuItem
disabled={!complianceConfirmed}
onClick={() => {
setCurrentRow(row.original)
setOpen('update')
@ -55,6 +56,7 @@ export function DataTableRowActions({ row }: DataTableRowActionsProps) {
{t('Edit')}
</DropdownMenuItem>
<DropdownMenuItem
disabled={!complianceConfirmed}
onClick={() => {
setCurrentRow(row.original)
setOpen('toggle-status')

View File

@ -23,10 +23,14 @@ import { useSubscriptions } from './subscriptions-provider'
export function SubscriptionsPrimaryButtons() {
const { t } = useTranslation()
const { setOpen } = useSubscriptions()
const { setOpen, complianceConfirmed } = useSubscriptions()
return (
<div className='flex gap-2'>
<Button size='sm' onClick={() => setOpen('create')}>
<Button
size='sm'
onClick={() => setOpen('create')}
disabled={!complianceConfirmed}
>
<Plus className='h-4 w-4' />
{t('Create Plan')}
</Button>

View File

@ -18,8 +18,14 @@ For commercial licensing, please contact support@quantumnous.com
*/
import React, { useState } from 'react'
import useDialogState from '@/hooks/use-dialog'
import {
getOptionValue,
useSystemOptions,
} from '@/features/system-settings/hooks/use-system-options'
import { type PlanRecord, type SubscriptionsDialogType } from '../types'
const CURRENT_COMPLIANCE_TERMS_VERSION = 'v1'
type SubscriptionsContextType = {
open: SubscriptionsDialogType | null
setOpen: (str: SubscriptionsDialogType | null) => void
@ -27,6 +33,7 @@ type SubscriptionsContextType = {
setCurrentRow: React.Dispatch<React.SetStateAction<PlanRecord | null>>
refreshTrigger: number
triggerRefresh: () => void
complianceConfirmed: boolean
}
const SubscriptionsContext =
@ -40,6 +47,15 @@ export function SubscriptionsProvider({
const [open, setOpen] = useDialogState<SubscriptionsDialogType>(null)
const [currentRow, setCurrentRow] = useState<PlanRecord | null>(null)
const [refreshTrigger, setRefreshTrigger] = useState(0)
const { data } = useSystemOptions()
const complianceOptions = getOptionValue(data?.data, {
'payment_setting.compliance_confirmed': false,
'payment_setting.compliance_terms_version': '',
})
const complianceConfirmed =
complianceOptions['payment_setting.compliance_confirmed'] &&
complianceOptions['payment_setting.compliance_terms_version'] ===
CURRENT_COMPLIANCE_TERMS_VERSION
const triggerRefresh = () => setRefreshTrigger((prev) => prev + 1)
@ -52,6 +68,7 @@ export function SubscriptionsProvider({
setCurrentRow,
refreshTrigger,
triggerRefresh,
complianceConfirmed,
}}
>
{children}

View File

@ -22,13 +22,18 @@ import { Alert, AlertDescription } from '@/components/ui/alert'
import { SectionPageLayout } from '@/components/layout'
import { SubscriptionsDialogs } from './components/subscriptions-dialogs'
import { SubscriptionsPrimaryButtons } from './components/subscriptions-primary-buttons'
import { SubscriptionsProvider } from './components/subscriptions-provider'
import {
SubscriptionsProvider,
useSubscriptions,
} from './components/subscriptions-provider'
import { SubscriptionsTable } from './components/subscriptions-table'
export function Subscriptions() {
function SubscriptionsContent() {
const { t } = useTranslation()
const { complianceConfirmed } = useSubscriptions()
return (
<SubscriptionsProvider>
<>
<SectionPageLayout>
<SectionPageLayout.Title>
{t('Subscription Management')}
@ -50,11 +55,28 @@ export function Subscriptions() {
</div>
</SectionPageLayout.Actions>
<SectionPageLayout.Content>
{!complianceConfirmed ? (
<Alert variant='destructive' className='mb-4'>
<AlertDescription>
{t(
'Subscription plan creation and changes are locked until the administrator confirms compliance terms in Payment Gateway settings.'
)}
</AlertDescription>
</Alert>
) : null}
<SubscriptionsTable />
</SectionPageLayout.Content>
</SectionPageLayout>
<SubscriptionsDialogs />
</>
)
}
export function Subscriptions() {
return (
<SubscriptionsProvider>
<SubscriptionsContent />
</SubscriptionsProvider>
)
}

View File

@ -18,6 +18,7 @@ For commercial licensing, please contact support@quantumnous.com
*/
import { api } from '@/lib/api'
import type {
ConfirmPaymentComplianceResponse,
DeleteLogsResponse,
FetchUpstreamRatiosRequest,
SystemOptionsResponse,
@ -37,6 +38,14 @@ export async function updateSystemOption(request: UpdateOptionRequest) {
return res.data
}
export async function confirmPaymentCompliance() {
const res = await api.post<ConfirmPaymentComplianceResponse>(
'/api/option/payment_compliance',
{ confirmed: true }
)
return res.data
}
export async function deleteLogsBefore(targetTimestamp: number) {
const res = await api.delete<DeleteLogsResponse>('/api/log/', {
params: { target_timestamp: targetTimestamp },

View File

@ -66,6 +66,11 @@ const defaultBillingSettings: BillingSettings = {
PayMethods: '',
'payment_setting.amount_options': '',
'payment_setting.amount_discount': '',
'payment_setting.compliance_confirmed': false,
'payment_setting.compliance_terms_version': '',
'payment_setting.compliance_confirmed_at': 0,
'payment_setting.compliance_confirmed_by': 0,
'payment_setting.compliance_confirmed_ip': '',
StripeApiSecret: '',
StripeWebhookSecret: '',
StripePriceId: '',

View File

@ -71,6 +71,10 @@ const BILLING_SECTIONS = [
settings['quota_setting.enable_free_model_pre_consume'],
},
}}
complianceConfirmed={
(settings['payment_setting.compliance_confirmed'] ?? false) &&
settings['payment_setting.compliance_terms_version'] === 'v1'
}
/>
),
},
@ -187,6 +191,13 @@ const BILLING_SECTIONS = [
WaffoPancakeUnitPrice: settings.WaffoPancakeUnitPrice ?? 1,
WaffoPancakeMinTopUp: settings.WaffoPancakeMinTopUp ?? 1,
}}
complianceDefaults={{
confirmed: settings['payment_setting.compliance_confirmed'] ?? false,
termsVersion:
settings['payment_setting.compliance_terms_version'] ?? '',
confirmedAt: settings['payment_setting.compliance_confirmed_at'] ?? 0,
confirmedBy: settings['payment_setting.compliance_confirmed_by'] ?? 0,
}}
/>
),
},

View File

@ -21,6 +21,7 @@ import * as z from 'zod'
import type { Resolver } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'
import { useTranslation } from 'react-i18next'
import { Alert, AlertDescription } from '@/components/ui/alert'
import { Button } from '@/components/ui/button'
import {
Form,
@ -57,10 +58,12 @@ type QuotaFormValues = z.infer<typeof quotaSchema>
type QuotaSettingsSectionProps = {
defaultValues: QuotaFormValues
complianceConfirmed?: boolean
}
export function QuotaSettingsSection({
defaultValues,
complianceConfirmed = true,
}: QuotaSettingsSectionProps) {
const { t } = useTranslation()
const updateOption = useUpdateOption()
@ -97,6 +100,16 @@ export function QuotaSettingsSection({
>
<FormNavigationGuard when={isDirty} />
{!complianceConfirmed ? (
<Alert variant='destructive'>
<AlertDescription>
{t(
'Non-zero invitation rewards require compliance confirmation in Payment Gateway settings.'
)}
</AlertDescription>
</Alert>
) : null}
<Form {...form}>
<form onSubmit={handleSubmit} className='space-y-6'>
<FormDirtyIndicator isDirty={isDirty} />

View File

@ -20,8 +20,17 @@ import * as React from 'react'
import * as z from 'zod'
import { useForm } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'
import { Code2, Eye } from 'lucide-react'
import { useMutation, useQueryClient } from '@tanstack/react-query'
import { Code2, Eye, ShieldAlert } from 'lucide-react'
import { useTranslation } from 'react-i18next'
import { toast } from 'sonner'
import { cn } from '@/lib/utils'
import {
Alert,
AlertAction,
AlertDescription,
AlertTitle,
} from '@/components/ui/alert'
import { Button } from '@/components/ui/button'
import {
Form,
@ -36,6 +45,8 @@ import { Input } from '@/components/ui/input'
import { Separator } from '@/components/ui/separator'
import { Switch } from '@/components/ui/switch'
import { Textarea } from '@/components/ui/textarea'
import { RiskAcknowledgementDialog } from '@/components/risk-acknowledgement-dialog'
import { confirmPaymentCompliance } from '../api'
import { SettingsSection } from '../components/settings-section'
import { useUpdateOption } from '../hooks/use-update-option'
import { AmountDiscountVisualEditor } from './amount-discount-visual-editor'
@ -125,18 +136,30 @@ const paymentSchema = z.object({
type PaymentFormValues = z.infer<typeof paymentSchema>
const CURRENT_COMPLIANCE_TERMS_VERSION = 'v1'
type PaymentComplianceDefaults = {
confirmed: boolean
termsVersion: string
confirmedAt: number
confirmedBy: number
}
type PaymentSettingsSectionProps = {
defaultValues: PaymentFormValues
waffoDefaultValues: WaffoSettingsValues
waffoPancakeDefaultValues: WaffoPancakeSettingsValues
complianceDefaults: PaymentComplianceDefaults
}
export function PaymentSettingsSection({
defaultValues,
waffoDefaultValues,
waffoPancakeDefaultValues,
complianceDefaults,
}: PaymentSettingsSectionProps) {
const { t } = useTranslation()
const queryClient = useQueryClient()
const updateOption = useUpdateOption()
const initialRef = React.useRef(defaultValues)
const defaultsSignature = React.useMemo(
@ -151,6 +174,81 @@ export function PaymentSettingsSection({
React.useState(true)
const [creemProductsVisualMode, setCreemProductsVisualMode] =
React.useState(true)
const [showComplianceDialog, setShowComplianceDialog] = React.useState(false)
const complianceStatements = React.useMemo(
() => [
t(
'You have legally obtained authorization for the connected model APIs, accounts, keys, and quotas.'
),
t(
'You commit to using upstream APIs, accounts, keys, quotas, and service capabilities only within the scope of lawful authorization obtained from upstream service providers, model service providers, or relevant rights holders, and will not conduct unauthorized resale, trafficking, distribution, or other non-compliant commercialization.'
),
t(
'If you provide generative AI services to the public in mainland China, you will fulfill legal obligations including filing, security assessment, content safety, complaint handling, generated content labeling, log retention, and personal information protection.'
),
t(
'You commit not to use this system to implement, assist with, or indirectly implement acts that violate applicable laws and regulations, regulatory requirements, platform rules, public interests, or the lawful rights and interests of third parties.'
),
t(
'You understand and independently bear legal responsibility arising from deployment, operation, and charging behavior.'
),
t(
'You understand this compliance reminder is only for risk notice and does not constitute legal advice, a compliance review conclusion, or a guarantee of the legality of your use of this system; you should consult professional legal or compliance advisors based on your actual business scenario.'
),
],
[t]
)
const complianceRequiredText = t(
'I have read and understood the above compliance reminder, acknowledge the related legal risks, and confirm that I bear legal responsibility arising from deployment, operation, and charging behavior.'
)
const complianceRequiredTextParts = React.useMemo(
() => [
{
type: 'input' as const,
text: t('I have read and understood the above compliance reminder'),
},
{ type: 'static' as const, text: t('') },
{
type: 'input' as const,
text: t('acknowledge the related legal risks'),
},
{ type: 'static' as const, text: t('and ') },
{
type: 'input' as const,
text: t(
'confirm that I bear legal responsibility arising from deployment'
),
},
{ type: 'static' as const, text: t('、') },
{
type: 'input' as const,
text: t('operation and charging behavior'),
},
],
[t]
)
const complianceConfirmed =
complianceDefaults.confirmed &&
complianceDefaults.termsVersion === CURRENT_COMPLIANCE_TERMS_VERSION
const confirmComplianceMutation = useMutation({
mutationFn: confirmPaymentCompliance,
onSuccess: (data) => {
if (data.success) {
toast.success(t('Compliance confirmed successfully'))
setShowComplianceDialog(false)
queryClient.invalidateQueries({ queryKey: ['system-options'] })
} else {
toast.error(data.message || t('Failed to confirm compliance'))
}
},
onError: (error: Error) => {
toast.error(error.message || t('Failed to confirm compliance'))
},
})
const form = useForm({
resolver: zodResolver(paymentSchema),
@ -562,11 +660,77 @@ export function PaymentSettingsSection({
'Configure recharge pricing and payment gateway integrations'
)}
>
{!complianceConfirmed ? (
<Alert variant='destructive' className='mb-6'>
<ShieldAlert className='h-4 w-4' />
<AlertTitle>{t('Compliance confirmation required')}</AlertTitle>
<AlertDescription>
<div className='space-y-3'>
<p>
{t(
'Payment, redemption codes, subscription plans, and invitation rewards are locked until the root administrator confirms the compliance terms.'
)}
</p>
<ol className='list-decimal space-y-1 pl-5'>
{complianceStatements.map((statement) => (
<li key={statement}>{statement}</li>
))}
</ol>
</div>
</AlertDescription>
<AlertAction>
<Button
type='button'
size='sm'
variant='destructive'
onClick={() => setShowComplianceDialog(true)}
>
{t('Confirm compliance')}
</Button>
</AlertAction>
</Alert>
) : (
<Alert className='mb-6'>
<AlertTitle>{t('Compliance confirmed')}</AlertTitle>
<AlertDescription>
{t('Confirmed at {{time}} by user #{{userId}}', {
time: complianceDefaults.confirmedAt
? new Date(
complianceDefaults.confirmedAt * 1000
).toLocaleString()
: '-',
userId: complianceDefaults.confirmedBy || '-',
})}
</AlertDescription>
</Alert>
)}
<RiskAcknowledgementDialog
open={showComplianceDialog}
onOpenChange={setShowComplianceDialog}
title={t('Confirm compliance terms')}
description={t(
'This confirmation unlocks payment, redemption code, subscription plan, and invitation reward features. Please read the statements carefully.'
)}
items={complianceStatements}
requiredText={complianceRequiredText}
requiredTextParts={complianceRequiredTextParts}
inputPrompt={t('Please type the following text to confirm:')}
inputPlaceholder={t('Type the confirmation text here')}
mismatchHint={t('The entered text does not match the required text.')}
confirmText={t('Confirm and enable')}
isLoading={confirmComplianceMutation.isPending}
onConfirm={() => confirmComplianceMutation.mutate()}
/>
{/* eslint-disable react-hooks/refs */}
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className='space-y-8'
className={cn(
'space-y-8',
!complianceConfirmed && 'pointer-events-none opacity-40'
)}
data-no-autosubmit='true'
>
<div className='space-y-4'>

View File

@ -39,6 +39,17 @@ export type UpdateOptionResponse = {
message: string
}
export type ConfirmPaymentComplianceResponse = {
success: boolean
message: string
data?: {
confirmed: boolean
terms_version: string
confirmed_at: number
confirmed_by: number
}
}
export type DeleteLogsResponse = {
success: boolean
message: string
@ -215,6 +226,11 @@ export type BillingSettings = {
PayMethods: string
'payment_setting.amount_options': string
'payment_setting.amount_discount': string
'payment_setting.compliance_confirmed': boolean
'payment_setting.compliance_terms_version': string
'payment_setting.compliance_confirmed_at': number
'payment_setting.compliance_confirmed_by': number
'payment_setting.compliance_confirmed_ip': string
StripeApiSecret: string
StripeWebhookSecret: string
StripePriceId: string

View File

@ -30,6 +30,7 @@ interface AffiliateRewardsCardProps {
user: UserWalletData | null
affiliateLink: string
onTransfer: () => void
complianceConfirmed?: boolean
loading?: boolean
}
@ -37,6 +38,7 @@ export function AffiliateRewardsCard({
user,
affiliateLink,
onTransfer,
complianceConfirmed = true,
loading,
}: AffiliateRewardsCardProps) {
const { t } = useTranslation()
@ -110,6 +112,7 @@ export function AffiliateRewardsCard({
{hasRewards && (
<Button
onClick={onTransfer}
disabled={!complianceConfirmed}
className='h-9 shrink-0 px-3'
size='sm'
>
@ -117,6 +120,13 @@ export function AffiliateRewardsCard({
</Button>
)}
</div>
{!complianceConfirmed ? (
<p className='text-muted-foreground text-xs lg:col-span-3'>
{t(
'Referral reward transfer is disabled until the administrator confirms compliance terms.'
)}
</p>
) : null}
</CardContent>
</Card>
)

View File

@ -135,6 +135,7 @@ export function RechargeFormCard({
const hasWaffoPaymentMethods =
Array.isArray(waffoPayMethods) && waffoPayMethods.length > 0
const minTopup = getMinTopupAmount(topupInfo)
const redemptionEnabled = topupInfo?.enable_redemption !== false
if (loading) {
return (
@ -445,49 +446,59 @@ export function RechargeFormCard({
)}
{/* Redemption Code Section */}
<div className='space-y-2.5 border-t pt-4 sm:space-y-3 sm:pt-6'>
<div className='flex items-center gap-2'>
<Gift className='text-muted-foreground h-4 w-4' />
<Label
htmlFor='redemption-code'
className='text-muted-foreground text-xs font-medium tracking-wider uppercase'
>
{t('Have a Code?')}
</Label>
</div>
<div className='grid grid-cols-[minmax(0,1fr)_auto] gap-2'>
<Input
id='redemption-code'
value={redemptionCode}
onChange={(e) => onRedemptionCodeChange(e.target.value)}
placeholder={t('Enter your redemption code')}
className='h-9 min-w-0'
/>
<Button
onClick={onRedeem}
disabled={redeeming}
variant='outline'
className='h-9 px-4'
>
{redeeming && <Loader2 className='mr-2 h-4 w-4 animate-spin' />}
{t('Redeem')}
</Button>
</div>
{topupLink && (
<p className='text-muted-foreground text-xs'>
{t('Need a redemption code?')}{' '}
<a
href={topupLink}
target='_blank'
rel='noopener noreferrer'
className='inline-flex items-center gap-1 underline-offset-4 hover:underline'
{redemptionEnabled ? (
<div className='space-y-2.5 border-t pt-4 sm:space-y-3 sm:pt-6'>
<div className='flex items-center gap-2'>
<Gift className='text-muted-foreground h-4 w-4' />
<Label
htmlFor='redemption-code'
className='text-muted-foreground text-xs font-medium tracking-wider uppercase'
>
{t('Get one here')}
<ExternalLink className='h-3 w-3' />
</a>
</p>
)}
</div>
{t('Have a Code?')}
</Label>
</div>
<div className='grid grid-cols-[minmax(0,1fr)_auto] gap-2'>
<Input
id='redemption-code'
value={redemptionCode}
onChange={(e) => onRedemptionCodeChange(e.target.value)}
placeholder={t('Enter your redemption code')}
className='h-9 min-w-0'
/>
<Button
onClick={onRedeem}
disabled={redeeming}
variant='outline'
className='h-9 px-4'
>
{redeeming && <Loader2 className='mr-2 h-4 w-4 animate-spin' />}
{t('Redeem')}
</Button>
</div>
{topupLink && (
<p className='text-muted-foreground text-xs'>
{t('Need a redemption code?')}{' '}
<a
href={topupLink}
target='_blank'
rel='noopener noreferrer'
className='inline-flex items-center gap-1 underline-offset-4 hover:underline'
>
{t('Get one here')}
<ExternalLink className='h-3 w-3' />
</a>
</p>
)}
</div>
) : (
<Alert className='border-t'>
<AlertDescription>
{t(
'Redemption codes are disabled until the administrator confirms compliance terms.'
)}
</AlertDescription>
</Alert>
)}
</TitledCard>
)
}

View File

@ -319,6 +319,9 @@ export function Wallet(props: WalletProps) {
user={user}
affiliateLink={affiliateLink}
onTransfer={() => setTransferDialogOpen(true)}
complianceConfirmed={
topupInfo?.payment_compliance_confirmed !== false
}
loading={affiliateLoading}
/>
</div>

View File

@ -145,6 +145,12 @@ export interface TopupInfo {
enable_waffo_pancake_topup?: boolean
/** Minimum topup amount for Waffo Pancake */
waffo_pancake_min_topup?: number
/** Whether redemption code usage is enabled */
enable_redemption?: boolean
/** Whether compliance confirmation has been completed */
payment_compliance_confirmed?: boolean
/** Current compliance terms version */
payment_compliance_terms_version?: string
}
/**

View File

@ -1756,7 +1756,7 @@
"footer.columns.related.links.oneApi": "One API",
"footer.columns.related.title": "Related Projects",
"footer.defaultCopyright": "All rights reserved.",
"footer.new\u0061pi.projectAttributionSuffix": "All rights reserved. Designed and developed by the project contributors.",
"footer.newapi.projectAttributionSuffix": "All rights reserved. Designed and developed by the project contributors.",
"For channels added after May 10, 2025, no need to remove \".\" from model names during deployment": "For channels added after May 10, 2025, no need to remove \".\" from model names during deployment",
"For private deployments, format: https://fastgpt.run/api/openapi": "For private deployments, format: https://fastgpt.run/api/openapi",
"Force a syntactically valid JSON response": "Force a syntactically valid JSON response",
@ -4423,6 +4423,37 @@
"Zero retention": "Zero retention",
"Zhipu": "Zhipu",
"Zhipu V4": "Zhipu V4",
"Zoom": "Zoom"
"Zoom": "Zoom",
"You have legally obtained authorization for the connected model APIs, accounts, keys, and quotas.": "You have legally obtained authorization for the connected model APIs, accounts, keys, and quotas.",
"You commit to using upstream APIs, accounts, keys, quotas, and service capabilities only within the scope of lawful authorization obtained from upstream service providers, model service providers, or relevant rights holders, and will not conduct unauthorized resale, trafficking, distribution, or other non-compliant commercialization.": "You commit to using upstream APIs, accounts, keys, quotas, and service capabilities only within the scope of lawful authorization obtained from upstream service providers, model service providers, or relevant rights holders, and will not conduct unauthorized resale, trafficking, distribution, or other non-compliant commercialization.",
"If you provide generative AI services to the public in mainland China, you will fulfill legal obligations including filing, security assessment, content safety, complaint handling, generated content labeling, log retention, and personal information protection.": "If you provide generative AI services to the public in mainland China, you will fulfill legal obligations including filing, security assessment, content safety, complaint handling, generated content labeling, log retention, and personal information protection.",
"You commit not to use this system to implement, assist with, or indirectly implement acts that violate applicable laws and regulations, regulatory requirements, platform rules, public interests, or the lawful rights and interests of third parties.": "You commit not to use this system to implement, assist with, or indirectly implement acts that violate applicable laws and regulations, regulatory requirements, platform rules, public interests, or the lawful rights and interests of third parties.",
"You understand and independently bear legal responsibility arising from deployment, operation, and charging behavior.": "You understand and independently bear legal responsibility arising from deployment, operation, and charging behavior.",
"You understand this compliance reminder is only for risk notice and does not constitute legal advice, a compliance review conclusion, or a guarantee of the legality of your use of this system; you should consult professional legal or compliance advisors based on your actual business scenario.": "You understand this compliance reminder is only for risk notice and does not constitute legal advice, a compliance review conclusion, or a guarantee of the legality of your use of this system; you should consult professional legal or compliance advisors based on your actual business scenario.",
"I have read and understood the above compliance reminder, acknowledge the related legal risks, and confirm that I bear legal responsibility arising from deployment, operation, and charging behavior.": "I have read and understood the above compliance reminder, acknowledge the related legal risks, and confirm that I bear legal responsibility arising from deployment, operation, and charging behavior.",
"Please type the following text to confirm:": "Please type the following text to confirm:",
"Type the confirmation text here": "Type the confirmation text here",
"The entered text does not match the required text.": "The entered text does not match the required text.",
"Compliance confirmation required": "Compliance confirmation required",
"Payment, redemption codes, subscription plans, and invitation rewards are locked until the root administrator confirms the compliance terms.": "Payment, redemption codes, subscription plans, and invitation rewards are locked until the root administrator confirms the compliance terms.",
"Confirm compliance": "Confirm compliance",
"Compliance confirmed": "Compliance confirmed",
"Confirmed at {{time}} by user #{{userId}}": "Confirmed at {{time}} by user #{{userId}}",
"Confirm compliance terms": "Confirm compliance terms",
"This confirmation unlocks payment, redemption code, subscription plan, and invitation reward features. Please read the statements carefully.": "This confirmation unlocks payment, redemption code, subscription plan, and invitation reward features. Please read the statements carefully.",
"Confirm and enable": "Confirm and enable",
"Compliance confirmed successfully": "Compliance confirmed successfully",
"Failed to confirm compliance": "Failed to confirm compliance",
"Redemption codes are disabled until the administrator confirms compliance terms.": "Redemption codes are disabled until the administrator confirms compliance terms.",
"Subscription plan creation and changes are locked until the administrator confirms compliance terms in Payment Gateway settings.": "Subscription plan creation and changes are locked until the administrator confirms compliance terms in Payment Gateway settings.",
"Referral reward transfer is disabled until the administrator confirms compliance terms.": "Referral reward transfer is disabled until the administrator confirms compliance terms.",
"Non-zero invitation rewards require compliance confirmation in Payment Gateway settings.": "Non-zero invitation rewards require compliance confirmation in Payment Gateway settings.",
"I have read and understood the above compliance reminder": "I have read and understood the above compliance reminder",
"acknowledge the related legal risks": "acknowledge the related legal risks",
"confirm that I bear legal responsibility arising from deployment": "confirm that I bear legal responsibility arising from deployment",
"operation and charging behavior": "operation and charging behavior",
"": ", ",
"and ": ", and ",
"、": ", "
}
}

View File

@ -1756,7 +1756,7 @@
"footer.columns.related.links.oneApi": "One API",
"footer.columns.related.title": "Projets liés",
"footer.defaultCopyright": "Tous droits réservés.",
"footer.new\u0061pi.projectAttributionSuffix": "Tous droits réservés. Conçu et développé par les contributeurs du projet.",
"footer.newapi.projectAttributionSuffix": "Tous droits réservés. Conçu et développé par les contributeurs du projet.",
"For channels added after May 10, 2025, no need to remove \".\" from model names during deployment": "Pour les canaux ajoutés après le 10 mai 2025, pas besoin de supprimer \".\" des noms de modèles lors du déploiement",
"For private deployments, format: https://fastgpt.run/api/openapi": "Pour les déploiements privés, format : https://fastgpt.run/api/openapi",
"Force a syntactically valid JSON response": "Imposer une réponse JSON syntaxiquement valide",
@ -4423,6 +4423,37 @@
"Zero retention": "Aucune rétention",
"Zhipu": "Zhipu",
"Zhipu V4": "Zhipu V4",
"Zoom": "Zoom"
"Zoom": "Zoom",
"You have legally obtained authorization for the connected model APIs, accounts, keys, and quotas.": "You have legally obtained authorization for the connected model APIs, accounts, keys, and quotas.",
"You commit to using upstream APIs, accounts, keys, quotas, and service capabilities only within the scope of lawful authorization obtained from upstream service providers, model service providers, or relevant rights holders, and will not conduct unauthorized resale, trafficking, distribution, or other non-compliant commercialization.": "You commit to using upstream APIs, accounts, keys, quotas, and service capabilities only within the scope of lawful authorization obtained from upstream service providers, model service providers, or relevant rights holders, and will not conduct unauthorized resale, trafficking, distribution, or other non-compliant commercialization.",
"If you provide generative AI services to the public in mainland China, you will fulfill legal obligations including filing, security assessment, content safety, complaint handling, generated content labeling, log retention, and personal information protection.": "If you provide generative AI services to the public in mainland China, you will fulfill legal obligations including filing, security assessment, content safety, complaint handling, generated content labeling, log retention, and personal information protection.",
"You commit not to use this system to implement, assist with, or indirectly implement acts that violate applicable laws and regulations, regulatory requirements, platform rules, public interests, or the lawful rights and interests of third parties.": "You commit not to use this system to implement, assist with, or indirectly implement acts that violate applicable laws and regulations, regulatory requirements, platform rules, public interests, or the lawful rights and interests of third parties.",
"You understand and independently bear legal responsibility arising from deployment, operation, and charging behavior.": "You understand and independently bear legal responsibility arising from deployment, operation, and charging behavior.",
"You understand this compliance reminder is only for risk notice and does not constitute legal advice, a compliance review conclusion, or a guarantee of the legality of your use of this system; you should consult professional legal or compliance advisors based on your actual business scenario.": "You understand this compliance reminder is only for risk notice and does not constitute legal advice, a compliance review conclusion, or a guarantee of the legality of your use of this system; you should consult professional legal or compliance advisors based on your actual business scenario.",
"I have read and understood the above compliance reminder, acknowledge the related legal risks, and confirm that I bear legal responsibility arising from deployment, operation, and charging behavior.": "I have read and understood the above compliance reminder, acknowledge the related legal risks, and confirm that I bear legal responsibility arising from deployment, operation, and charging behavior.",
"Please type the following text to confirm:": "Please type the following text to confirm:",
"Type the confirmation text here": "Type the confirmation text here",
"The entered text does not match the required text.": "The entered text does not match the required text.",
"Compliance confirmation required": "Compliance confirmation required",
"Payment, redemption codes, subscription plans, and invitation rewards are locked until the root administrator confirms the compliance terms.": "Payment, redemption codes, subscription plans, and invitation rewards are locked until the root administrator confirms the compliance terms.",
"Confirm compliance": "Confirm compliance",
"Compliance confirmed": "Compliance confirmed",
"Confirmed at {{time}} by user #{{userId}}": "Confirmed at {{time}} by user #{{userId}}",
"Confirm compliance terms": "Confirm compliance terms",
"This confirmation unlocks payment, redemption code, subscription plan, and invitation reward features. Please read the statements carefully.": "This confirmation unlocks payment, redemption code, subscription plan, and invitation reward features. Please read the statements carefully.",
"Confirm and enable": "Confirm and enable",
"Compliance confirmed successfully": "Compliance confirmed successfully",
"Failed to confirm compliance": "Failed to confirm compliance",
"Redemption codes are disabled until the administrator confirms compliance terms.": "Redemption codes are disabled until the administrator confirms compliance terms.",
"Subscription plan creation and changes are locked until the administrator confirms compliance terms in Payment Gateway settings.": "Subscription plan creation and changes are locked until the administrator confirms compliance terms in Payment Gateway settings.",
"Referral reward transfer is disabled until the administrator confirms compliance terms.": "Referral reward transfer is disabled until the administrator confirms compliance terms.",
"Non-zero invitation rewards require compliance confirmation in Payment Gateway settings.": "Non-zero invitation rewards require compliance confirmation in Payment Gateway settings.",
"I have read and understood the above compliance reminder": "I have read and understood the above compliance reminder",
"acknowledge the related legal risks": "acknowledge the related legal risks",
"confirm that I bear legal responsibility arising from deployment": "confirm that I bear legal responsibility arising from deployment",
"operation and charging behavior": "operation and charging behavior",
"": ", ",
"and ": ", and ",
"、": ", "
}
}

View File

@ -1756,7 +1756,7 @@
"footer.columns.related.links.oneApi": "1つのAPI",
"footer.columns.related.title": "関連プロジェクト",
"footer.defaultCopyright": "すべての権利を留保します。",
"footer.new\u0061pi.projectAttributionSuffix": "すべての権利を留保します。プロジェクトコントリビューターにより設計・開発されています。",
"footer.newapi.projectAttributionSuffix": "すべての権利を留保します。プロジェクトコントリビューターにより設計・開発されています。",
"For channels added after May 10, 2025, no need to remove \".\" from model names during deployment": "2025 年 5 月 10 日以降に追加されたチャネルの場合、デプロイ時にモデル名から「.」を削除する必要はありません",
"For private deployments, format: https://fastgpt.run/api/openapi": "プライベートデプロイメントの場合、形式: https://fastgpt.run/api/openapi",
"Force a syntactically valid JSON response": "構文的に有効な JSON 応答を強制",
@ -4423,6 +4423,37 @@
"Zero retention": "データ保持なし",
"Zhipu": "Zhipu",
"Zhipu V4": "Zhipu V 4",
"Zoom": "ズーム"
"Zoom": "ズーム",
"You have legally obtained authorization for the connected model APIs, accounts, keys, and quotas.": "You have legally obtained authorization for the connected model APIs, accounts, keys, and quotas.",
"You commit to using upstream APIs, accounts, keys, quotas, and service capabilities only within the scope of lawful authorization obtained from upstream service providers, model service providers, or relevant rights holders, and will not conduct unauthorized resale, trafficking, distribution, or other non-compliant commercialization.": "You commit to using upstream APIs, accounts, keys, quotas, and service capabilities only within the scope of lawful authorization obtained from upstream service providers, model service providers, or relevant rights holders, and will not conduct unauthorized resale, trafficking, distribution, or other non-compliant commercialization.",
"If you provide generative AI services to the public in mainland China, you will fulfill legal obligations including filing, security assessment, content safety, complaint handling, generated content labeling, log retention, and personal information protection.": "If you provide generative AI services to the public in mainland China, you will fulfill legal obligations including filing, security assessment, content safety, complaint handling, generated content labeling, log retention, and personal information protection.",
"You commit not to use this system to implement, assist with, or indirectly implement acts that violate applicable laws and regulations, regulatory requirements, platform rules, public interests, or the lawful rights and interests of third parties.": "You commit not to use this system to implement, assist with, or indirectly implement acts that violate applicable laws and regulations, regulatory requirements, platform rules, public interests, or the lawful rights and interests of third parties.",
"You understand and independently bear legal responsibility arising from deployment, operation, and charging behavior.": "You understand and independently bear legal responsibility arising from deployment, operation, and charging behavior.",
"You understand this compliance reminder is only for risk notice and does not constitute legal advice, a compliance review conclusion, or a guarantee of the legality of your use of this system; you should consult professional legal or compliance advisors based on your actual business scenario.": "You understand this compliance reminder is only for risk notice and does not constitute legal advice, a compliance review conclusion, or a guarantee of the legality of your use of this system; you should consult professional legal or compliance advisors based on your actual business scenario.",
"I have read and understood the above compliance reminder, acknowledge the related legal risks, and confirm that I bear legal responsibility arising from deployment, operation, and charging behavior.": "I have read and understood the above compliance reminder, acknowledge the related legal risks, and confirm that I bear legal responsibility arising from deployment, operation, and charging behavior.",
"Please type the following text to confirm:": "Please type the following text to confirm:",
"Type the confirmation text here": "Type the confirmation text here",
"The entered text does not match the required text.": "The entered text does not match the required text.",
"Compliance confirmation required": "Compliance confirmation required",
"Payment, redemption codes, subscription plans, and invitation rewards are locked until the root administrator confirms the compliance terms.": "Payment, redemption codes, subscription plans, and invitation rewards are locked until the root administrator confirms the compliance terms.",
"Confirm compliance": "Confirm compliance",
"Compliance confirmed": "Compliance confirmed",
"Confirmed at {{time}} by user #{{userId}}": "Confirmed at {{time}} by user #{{userId}}",
"Confirm compliance terms": "Confirm compliance terms",
"This confirmation unlocks payment, redemption code, subscription plan, and invitation reward features. Please read the statements carefully.": "This confirmation unlocks payment, redemption code, subscription plan, and invitation reward features. Please read the statements carefully.",
"Confirm and enable": "Confirm and enable",
"Compliance confirmed successfully": "Compliance confirmed successfully",
"Failed to confirm compliance": "Failed to confirm compliance",
"Redemption codes are disabled until the administrator confirms compliance terms.": "Redemption codes are disabled until the administrator confirms compliance terms.",
"Subscription plan creation and changes are locked until the administrator confirms compliance terms in Payment Gateway settings.": "Subscription plan creation and changes are locked until the administrator confirms compliance terms in Payment Gateway settings.",
"Referral reward transfer is disabled until the administrator confirms compliance terms.": "Referral reward transfer is disabled until the administrator confirms compliance terms.",
"Non-zero invitation rewards require compliance confirmation in Payment Gateway settings.": "Non-zero invitation rewards require compliance confirmation in Payment Gateway settings.",
"I have read and understood the above compliance reminder": "I have read and understood the above compliance reminder",
"acknowledge the related legal risks": "acknowledge the related legal risks",
"confirm that I bear legal responsibility arising from deployment": "confirm that I bear legal responsibility arising from deployment",
"operation and charging behavior": "operation and charging behavior",
"": ", ",
"and ": ", and ",
"、": ", "
}
}

View File

@ -1756,7 +1756,7 @@
"footer.columns.related.links.oneApi": "Один API",
"footer.columns.related.title": "Связанные проекты",
"footer.defaultCopyright": "Все права защищены.",
"footer.new\u0061pi.projectAttributionSuffix": "Все права защищены. Разработано участниками проекта.",
"footer.newapi.projectAttributionSuffix": "Все права защищены. Разработано участниками проекта.",
"For channels added after May 10, 2025, no need to remove \".\" from model names during deployment": "Для каналов, добавленных после 10 мая 2025 г., не нужно удалять \".\" из имён моделей при развёртывании",
"For private deployments, format: https://fastgpt.run/api/openapi": "Для частных развертываний, формат: https://fastgpt.run/api/openapi",
"Force a syntactically valid JSON response": "Принудительно возвращать синтаксически корректный JSON",
@ -4423,6 +4423,37 @@
"Zero retention": "Без хранения данных",
"Zhipu": "Zhipu",
"Zhipu V4": "Zhipu V4",
"Zoom": "Zoom"
"Zoom": "Zoom",
"You have legally obtained authorization for the connected model APIs, accounts, keys, and quotas.": "You have legally obtained authorization for the connected model APIs, accounts, keys, and quotas.",
"You commit to using upstream APIs, accounts, keys, quotas, and service capabilities only within the scope of lawful authorization obtained from upstream service providers, model service providers, or relevant rights holders, and will not conduct unauthorized resale, trafficking, distribution, or other non-compliant commercialization.": "You commit to using upstream APIs, accounts, keys, quotas, and service capabilities only within the scope of lawful authorization obtained from upstream service providers, model service providers, or relevant rights holders, and will not conduct unauthorized resale, trafficking, distribution, or other non-compliant commercialization.",
"If you provide generative AI services to the public in mainland China, you will fulfill legal obligations including filing, security assessment, content safety, complaint handling, generated content labeling, log retention, and personal information protection.": "If you provide generative AI services to the public in mainland China, you will fulfill legal obligations including filing, security assessment, content safety, complaint handling, generated content labeling, log retention, and personal information protection.",
"You commit not to use this system to implement, assist with, or indirectly implement acts that violate applicable laws and regulations, regulatory requirements, platform rules, public interests, or the lawful rights and interests of third parties.": "You commit not to use this system to implement, assist with, or indirectly implement acts that violate applicable laws and regulations, regulatory requirements, platform rules, public interests, or the lawful rights and interests of third parties.",
"You understand and independently bear legal responsibility arising from deployment, operation, and charging behavior.": "You understand and independently bear legal responsibility arising from deployment, operation, and charging behavior.",
"You understand this compliance reminder is only for risk notice and does not constitute legal advice, a compliance review conclusion, or a guarantee of the legality of your use of this system; you should consult professional legal or compliance advisors based on your actual business scenario.": "You understand this compliance reminder is only for risk notice and does not constitute legal advice, a compliance review conclusion, or a guarantee of the legality of your use of this system; you should consult professional legal or compliance advisors based on your actual business scenario.",
"I have read and understood the above compliance reminder, acknowledge the related legal risks, and confirm that I bear legal responsibility arising from deployment, operation, and charging behavior.": "I have read and understood the above compliance reminder, acknowledge the related legal risks, and confirm that I bear legal responsibility arising from deployment, operation, and charging behavior.",
"Please type the following text to confirm:": "Please type the following text to confirm:",
"Type the confirmation text here": "Type the confirmation text here",
"The entered text does not match the required text.": "The entered text does not match the required text.",
"Compliance confirmation required": "Compliance confirmation required",
"Payment, redemption codes, subscription plans, and invitation rewards are locked until the root administrator confirms the compliance terms.": "Payment, redemption codes, subscription plans, and invitation rewards are locked until the root administrator confirms the compliance terms.",
"Confirm compliance": "Confirm compliance",
"Compliance confirmed": "Compliance confirmed",
"Confirmed at {{time}} by user #{{userId}}": "Confirmed at {{time}} by user #{{userId}}",
"Confirm compliance terms": "Confirm compliance terms",
"This confirmation unlocks payment, redemption code, subscription plan, and invitation reward features. Please read the statements carefully.": "This confirmation unlocks payment, redemption code, subscription plan, and invitation reward features. Please read the statements carefully.",
"Confirm and enable": "Confirm and enable",
"Compliance confirmed successfully": "Compliance confirmed successfully",
"Failed to confirm compliance": "Failed to confirm compliance",
"Redemption codes are disabled until the administrator confirms compliance terms.": "Redemption codes are disabled until the administrator confirms compliance terms.",
"Subscription plan creation and changes are locked until the administrator confirms compliance terms in Payment Gateway settings.": "Subscription plan creation and changes are locked until the administrator confirms compliance terms in Payment Gateway settings.",
"Referral reward transfer is disabled until the administrator confirms compliance terms.": "Referral reward transfer is disabled until the administrator confirms compliance terms.",
"Non-zero invitation rewards require compliance confirmation in Payment Gateway settings.": "Non-zero invitation rewards require compliance confirmation in Payment Gateway settings.",
"I have read and understood the above compliance reminder": "I have read and understood the above compliance reminder",
"acknowledge the related legal risks": "acknowledge the related legal risks",
"confirm that I bear legal responsibility arising from deployment": "confirm that I bear legal responsibility arising from deployment",
"operation and charging behavior": "operation and charging behavior",
"": ", ",
"and ": ", and ",
"、": ", "
}
}

View File

@ -1756,7 +1756,7 @@
"footer.columns.related.links.oneApi": "One API",
"footer.columns.related.title": "Các Dự Án Liên Quan",
"footer.defaultCopyright": "Bản quyền được bảo lưu.",
"footer.new\u0061pi.projectAttributionSuffix": "Bản quyền được bảo lưu. Được thiết kế và phát triển bởi các cộng tác viên dự án.",
"footer.newapi.projectAttributionSuffix": "Bản quyền được bảo lưu. Được thiết kế và phát triển bởi các cộng tác viên dự án.",
"For channels added after May 10, 2025, no need to remove \".\" from model names during deployment": "Đối với các kênh được thêm sau ngày 10 tháng 5 năm 2025, không cần loại bỏ \".\" khỏi tên mô hình trong quá trình triển khai",
"For private deployments, format: https://fastgpt.run/api/openapi": "Đối với các triển khai riêng tư, định dạng: https://fastgpt.run/api/openapi",
"Force a syntactically valid JSON response": "Buộc phản hồi JSON hợp lệ về cú pháp",
@ -4423,6 +4423,37 @@
"Zero retention": "Không lưu dữ liệu",
"Zhipu": "Zhipu",
"Zhipu V4": "Zhipu V4",
"Zoom": "Zoom"
"Zoom": "Zoom",
"You have legally obtained authorization for the connected model APIs, accounts, keys, and quotas.": "You have legally obtained authorization for the connected model APIs, accounts, keys, and quotas.",
"You commit to using upstream APIs, accounts, keys, quotas, and service capabilities only within the scope of lawful authorization obtained from upstream service providers, model service providers, or relevant rights holders, and will not conduct unauthorized resale, trafficking, distribution, or other non-compliant commercialization.": "You commit to using upstream APIs, accounts, keys, quotas, and service capabilities only within the scope of lawful authorization obtained from upstream service providers, model service providers, or relevant rights holders, and will not conduct unauthorized resale, trafficking, distribution, or other non-compliant commercialization.",
"If you provide generative AI services to the public in mainland China, you will fulfill legal obligations including filing, security assessment, content safety, complaint handling, generated content labeling, log retention, and personal information protection.": "If you provide generative AI services to the public in mainland China, you will fulfill legal obligations including filing, security assessment, content safety, complaint handling, generated content labeling, log retention, and personal information protection.",
"You commit not to use this system to implement, assist with, or indirectly implement acts that violate applicable laws and regulations, regulatory requirements, platform rules, public interests, or the lawful rights and interests of third parties.": "You commit not to use this system to implement, assist with, or indirectly implement acts that violate applicable laws and regulations, regulatory requirements, platform rules, public interests, or the lawful rights and interests of third parties.",
"You understand and independently bear legal responsibility arising from deployment, operation, and charging behavior.": "You understand and independently bear legal responsibility arising from deployment, operation, and charging behavior.",
"You understand this compliance reminder is only for risk notice and does not constitute legal advice, a compliance review conclusion, or a guarantee of the legality of your use of this system; you should consult professional legal or compliance advisors based on your actual business scenario.": "You understand this compliance reminder is only for risk notice and does not constitute legal advice, a compliance review conclusion, or a guarantee of the legality of your use of this system; you should consult professional legal or compliance advisors based on your actual business scenario.",
"I have read and understood the above compliance reminder, acknowledge the related legal risks, and confirm that I bear legal responsibility arising from deployment, operation, and charging behavior.": "I have read and understood the above compliance reminder, acknowledge the related legal risks, and confirm that I bear legal responsibility arising from deployment, operation, and charging behavior.",
"Please type the following text to confirm:": "Please type the following text to confirm:",
"Type the confirmation text here": "Type the confirmation text here",
"The entered text does not match the required text.": "The entered text does not match the required text.",
"Compliance confirmation required": "Compliance confirmation required",
"Payment, redemption codes, subscription plans, and invitation rewards are locked until the root administrator confirms the compliance terms.": "Payment, redemption codes, subscription plans, and invitation rewards are locked until the root administrator confirms the compliance terms.",
"Confirm compliance": "Confirm compliance",
"Compliance confirmed": "Compliance confirmed",
"Confirmed at {{time}} by user #{{userId}}": "Confirmed at {{time}} by user #{{userId}}",
"Confirm compliance terms": "Confirm compliance terms",
"This confirmation unlocks payment, redemption code, subscription plan, and invitation reward features. Please read the statements carefully.": "This confirmation unlocks payment, redemption code, subscription plan, and invitation reward features. Please read the statements carefully.",
"Confirm and enable": "Confirm and enable",
"Compliance confirmed successfully": "Compliance confirmed successfully",
"Failed to confirm compliance": "Failed to confirm compliance",
"Redemption codes are disabled until the administrator confirms compliance terms.": "Redemption codes are disabled until the administrator confirms compliance terms.",
"Subscription plan creation and changes are locked until the administrator confirms compliance terms in Payment Gateway settings.": "Subscription plan creation and changes are locked until the administrator confirms compliance terms in Payment Gateway settings.",
"Referral reward transfer is disabled until the administrator confirms compliance terms.": "Referral reward transfer is disabled until the administrator confirms compliance terms.",
"Non-zero invitation rewards require compliance confirmation in Payment Gateway settings.": "Non-zero invitation rewards require compliance confirmation in Payment Gateway settings.",
"I have read and understood the above compliance reminder": "I have read and understood the above compliance reminder",
"acknowledge the related legal risks": "acknowledge the related legal risks",
"confirm that I bear legal responsibility arising from deployment": "confirm that I bear legal responsibility arising from deployment",
"operation and charging behavior": "operation and charging behavior",
"": ", ",
"and ": ", and ",
"、": ", "
}
}

View File

@ -1756,7 +1756,7 @@
"footer.columns.related.links.oneApi": "One API",
"footer.columns.related.title": "相关项目",
"footer.defaultCopyright": "版权所有。",
"footer.new\u0061pi.projectAttributionSuffix": "版权所有,由项目贡献者设计与开发。",
"footer.newapi.projectAttributionSuffix": "版权所有,由项目贡献者设计与开发。",
"For channels added after May 10, 2025, no need to remove \".\" from model names during deployment": "对于 2025 年 5 月 10 日之后添加的渠道,在部署时无需从模型名称中移除 \".\"",
"For private deployments, format: https://fastgpt.run/api/openapi": "对于私有部署格式为https://fastgpt.run/api/openapi",
"Force a syntactically valid JSON response": "强制返回语法合法的 JSON",
@ -4423,6 +4423,37 @@
"Zero retention": "零数据保留",
"Zhipu": "智谱",
"Zhipu V4": "智谱 V4",
"Zoom": "缩放"
"Zoom": "缩放",
"You have legally obtained authorization for the connected model APIs, accounts, keys, and quotas.": "你已合法取得所接入模型 API、账号、密钥和额度的授权。",
"You commit to using upstream APIs, accounts, keys, quotas, and service capabilities only within the scope of lawful authorization obtained from upstream service providers, model service providers, or relevant rights holders, and will not conduct unauthorized resale, trafficking, distribution, or other non-compliant commercialization.": "你承诺仅在已取得上游服务商、模型服务提供方或相关权利方合法授权的范围内使用其 API、账号、密钥、额度及服务能力不进行未经授权的转售、倒卖、分销或其他违规商业化使用。",
"If you provide generative AI services to the public in mainland China, you will fulfill legal obligations including filing, security assessment, content safety, complaint handling, generated content labeling, log retention, and personal information protection.": "如向中华人民共和国境内公众提供生成式人工智能服务,你将依法履行备案登记、安全评估、内容安全、投诉举报、生成合成内容标识、日志留存、个人信息保护等合规义务。",
"You commit not to use this system to implement, assist with, or indirectly implement acts that violate applicable laws and regulations, regulatory requirements, platform rules, public interests, or the lawful rights and interests of third parties.": "你承诺不会利用本系统实施、协助实施或变相实施违反适用法律法规、监管要求、平台规则、社会公共利益或第三方合法权益的行为。",
"You understand and independently bear legal responsibility arising from deployment, operation, and charging behavior.": "你理解并自行承担部署、运营和收费行为产生的法律责任。",
"You understand this compliance reminder is only for risk notice and does not constitute legal advice, a compliance review conclusion, or a guarantee of the legality of your use of this system; you should consult professional legal or compliance advisors based on your actual business scenario.": "你理解本合规提醒仅用于风险提示,不构成法律意见、合规审查结论或对你使用本系统行为合法性的保证;你应根据实际业务场景自行咨询专业法律或合规顾问。",
"I have read and understood the above compliance reminder, acknowledge the related legal risks, and confirm that I bear legal responsibility arising from deployment, operation, and charging behavior.": "我已阅读并理解上述合规提醒,知悉相关法律风险,并确认自行承担部署、运营和收费行为产生的法律责任",
"Please type the following text to confirm:": "请输入以下文字以确认:",
"Type the confirmation text here": "请输入确认文案",
"The entered text does not match the required text.": "输入内容与要求文案不一致。",
"Compliance confirmation required": "需要确认合规声明",
"Payment, redemption codes, subscription plans, and invitation rewards are locked until the root administrator confirms the compliance terms.": "确认前,支付、兑换码、订阅计划和邀请返利功能将保持锁定。",
"Confirm compliance": "确认合规声明",
"Compliance confirmed": "合规声明已确认",
"Confirmed at {{time}} by user #{{userId}}": "确认时间:{{time}},确认用户:#{{userId}}",
"Confirm compliance terms": "确认合规声明",
"This confirmation unlocks payment, redemption code, subscription plan, and invitation reward features. Please read the statements carefully.": "该操作将启用支付、兑换码、订阅计划和邀请返利相关功能。请仔细阅读并确认以下声明。",
"Confirm and enable": "确认并启用",
"Compliance confirmed successfully": "合规声明确认成功",
"Failed to confirm compliance": "确认失败",
"Redemption codes are disabled until the administrator confirms compliance terms.": "兑换码功能已禁用,管理员需先确认合规声明。",
"Subscription plan creation and changes are locked until the administrator confirms compliance terms in Payment Gateway settings.": "订阅套餐创建和变更已锁定,管理员需先在支付设置中确认合规声明。",
"Referral reward transfer is disabled until the administrator confirms compliance terms.": "邀请奖励划转已禁用,管理员需先确认合规声明。",
"Non-zero invitation rewards require compliance confirmation in Payment Gateway settings.": "设置非零邀请奖励额度前,需要先在支付设置中确认合规声明。",
"I have read and understood the above compliance reminder": "我已阅读并理解上述合规提醒",
"acknowledge the related legal risks": "知悉相关法律风险",
"confirm that I bear legal responsibility arising from deployment": "并确认自行承担部署",
"operation and charging behavior": "运营和收费行为产生的法律责任",
"": "",
"and ": "",
"、": "、"
}
}