feat(quality-owner): 增加开通协议与强制阅读

This commit is contained in:
yml2213
2026-11-05 11:45:02 +08:00
parent 15030237c4
commit 09f244a93d
13 changed files with 735 additions and 50 deletions
+180 -11
View File
@@ -3,26 +3,33 @@ package qualityowner
import (
"encoding/json"
"errors"
"strings"
"time"
)
const (
ConfigKey = "quality_owner.rules"
StatusNone = "none"
StatusActive = "active"
DepositSourcePaid = "paid"
DepositSourceManual = "manual"
defaultDepositCent = int64(20_000)
defaultInsuranceBps = 800
defaultPremiumRateBps = 400
ConfigKey = "quality_owner.rules"
StatusNone = "none"
StatusActive = "active"
DepositSourcePaid = "paid"
DepositSourceManual = "manual"
defaultDepositCent = int64(20_000)
defaultInsuranceBps = 800
defaultPremiumRateBps = 400
defaultAgreementReadSeconds = 15
defaultAgreementTitle = "优质号主服务协议"
MaxAgreementReadSeconds = 300
)
var ErrInvalidRules = errors.New("invalid quality owner rules")
type Rules struct {
DepositAmountCent int64 `json:"deposit_amount_cent"`
InsuranceFeeBps int `json:"insurance_fee_bps"`
PremiumInsuranceFeeBps int `json:"premium_insurance_fee_bps"`
DepositAmountCent int64 `json:"deposit_amount_cent"`
InsuranceFeeBps int `json:"insurance_fee_bps"`
PremiumInsuranceFeeBps int `json:"premium_insurance_fee_bps"`
AgreementTitle string `json:"agreement_title"`
AgreementContent string `json:"agreement_content"`
AgreementReadSeconds int `json:"agreement_read_seconds"`
}
func DefaultRules() Rules {
@@ -30,6 +37,156 @@ func DefaultRules() Rules {
DepositAmountCent: defaultDepositCent,
InsuranceFeeBps: defaultInsuranceBps,
PremiumInsuranceFeeBps: defaultPremiumRateBps,
AgreementTitle: defaultAgreementTitle,
AgreementReadSeconds: defaultAgreementReadSeconds,
AgreementContent: `本协议由大锤商行平台(以下简称“平台”)与申请成为“优质号主”的用户(以下简称“号主”)共同签署,自号主勾选同意并完成信用保证金冻结之日起生效。
一、总则
1.1 为保障平台账号租赁交易的秩序与安全,平台推出「优质号主」认证计划,旨在筛选信誉良好、服务高效的号主,为其提供专属权益与曝光资源,包括将账号发布至「极品专区」并享有专项收益比例等。
1.2 号主自愿申请成为优质号主,并承诺遵守本协议全部条款。
二、定义
| 术语 | 定义 |
|---|---|
| 优质号主 | 通过平台认证、完成200元信用保证金冻结并遵守本协议的高信誉号主 |
| 《极品专区》 | 优质号主可选择发布的专属渠道,仅限大锤商行至尊SVIP用户租用;优质号主在该渠道发布账号可享有专项收益比例及专属标识 |
| 《大锤商行》 | 优质号主可选择发布的公开渠道,面向全平台注册用户租用 |
| 信用保证金(押金) | 号主为获得优质号主发布资格而授权平台冻结的200元信用保证金,仅冻结、不扣除 |
| 大锤商行至尊SVIP | 平台最高等级VIP用户,须同时满足租客免押资格方可租用《极品专区》渠道账号 |
三、成为优质号主的条件
3.1 信用保证金标准
本计划仅设一个信用保证金档位,所有申请者统一适用:
| 申请类型 | 信用保证金 | 备注 |
|---|---:|---|
| 所有申请者 | 200元 | 仅冻结,不扣除;不设其他金额档位或免押档位 |
3.2 强制前置条件
所有申请者须强制阅读《账号出租号主须知》满15秒,方可继续申请流程。
3.3 信用保证金特别约定
3.3.1 保证金性质:200元为信用保证金,仅用于信用展示及发布资格核验,不属于租赁费用、平台服务收入、违约金或处罚金。平台不扣除、不没收该保证金。
3.3.2 冻结方式:号主申请成为优质号主时,授权平台冻结200元信用保证金。冻结期间,该保证金不参与扣罚。
3.3.3 解冻条件:号主在无正在进行中的订单且无未解决的纠纷时,可随时向平台申请解冻全部信用保证金。平台审核通过后,按原支付路径或平台规则允许的方式解冻退还。
3.3.4 校验机制:每次发布账号时,系统自动核验信用保证金是否足额冻结:
- 已足额冻结 → 可正常发布;
- 未冻结或冻结不足 → 不可发布,系统弹窗提示“请先冻结200元信用保证金”。
3.3.5 违规处理与保证金:号主单方面违规的,平台有权依据本协议及平台规则采取警告、下架账号、限制发布、取消优质号主资格等措施,但信用保证金不扣除。如存在未完成订单或未解决纠纷,信用保证金可继续冻结至相关事项处理完毕。
四、优质号主的权利
成功成为优质号主后,享有以下权益:
| 序号 | 权益内容 | 具体说明 |
|---|---|---|
| 4.1 | 专项收益比例 | 优质号主将账号发布至「极品专区」的,享有专项收益比例:号主到手金额比例较普通渠道上浮1个百分点(即平台服务费比例相应下调1个百分点,具体以发布页面公示为准);该权益可与VIP权益及优惠券叠加使用 |
| 4.2 | 发布渠道选择 | 发布时可自由选择投放至「极品专区」或「大锤商行」(详见第五条) |
| 4.3 | 特殊曝光标识 | 优质号主发布的账号显示“优质号主”专属标识;发布至「极品专区」的账号同时展示“极品专区”专属标签 |
| 4.4 | 包赔手续费减半 | 购买全额包赔服务,手续费由8%降至4% |
| 4.5 | 平台兜底保障 | 非因号主明显过错导致的问题(如租客恶意损坏账号等),由平台按规则负责兜底赔偿 |
五、发布渠道规则
优质号主在每次发布账号时,必须从以下两个渠道中二选一:
5.1 渠道对比
| 对比项 | 《极品专区》 | 《大锤商行》 |
|---|---|---|
| 租用资格 | 仅限大锤商行至尊SVIP,且须具备租客免押资格 | 全平台注册用户 |
| 租客筛选标准 | 累计消费≥1万元 + 无纠纷记录 + 无封禁记录 | 无特殊限制 |
| 适用场景 | 高客单价、极致安全、专项收益比例 | 高曝光、快速出单 |
5.2 关于《极品专区》渠道
5.2.1 选择该渠道的账号,将展示“极品专区”专属标签。
5.2.2 非大锤商行至尊SVIP用户,或虽为大锤商行至尊SVIP但无租客免押资格的,不可查看及租用该渠道账号。
5.2.3 该渠道租客均为平台严选的优质租客,号主可享受“基本绝对安全”的出租体验。
5.2.4 优质号主在该渠道发布账号,可按本协议第4.1款享有专项收益比例。
5.3 关于《大锤商行》渠道
5.3.1 选择该渠道的账号,面向全体平台注册用户开放租用。
5.3.2 号主仍享有平台兜底保障权益(详见第4.5款)。
六、号主的义务与违规处理
6.1 号主的基本义务
优质号主在交易中应当:
6.1.1 按照约定时间交付账号,不得无故毁约、爽约;
6.1.2 在租客使用过程中,及时配合上号验证;
6.1.3 在需要时,积极配合完成人脸识别解除等操作;
6.1.4 不得在出租期间擅自顶号、修改密码或通过其他方式收回账号;
6.1.5 与租客保持友善沟通,不得出现辱骂、威胁等不文明行为。
6.2 违规行为与处罚
平台对违规行为采取警告、下架、限制发布、取消资格等措施;信用保证金不扣除。
| 违规行为 | 处理措施 |
|---|---|
| 顶号 | 警告、立即下架、限制发布;情节严重者取消优质号主资格 |
| 毁约不出租 | 警告、下架、限制发布;情节严重者取消优质号主资格 |
| 无法及时配合上号 | 警告、限制发布;多次违规者取消资格 |
| 无法配合解除人脸 | 警告、下架、限制发布;情节严重者取消资格 |
| 其他违反平台规则的行为 | 依据平台规则处理 |
上述处理均不扣除信用保证金;如存在未完成订单或未解决纠纷,信用保证金可继续冻结至相关事项处理完毕。
七、收益与结算
7.1 号主通过本平台获得的租赁收入,平台将按对应渠道的结算规则进行结算。
7.2 优质号主将账号发布至「极品专区」的,按本协议第4.1款享有专项收益比例;该权益可与VIP权益及优惠券叠加使用。
7.3 平台将在租客确认收货后将收益划转至号主账户余额,号主可随时申请提现。
八、免责条款
8.1 因不可抗力(包括但不限于自然灾害、战争、罢工、政府行为、网络故障、系统维护等)导致协议无法履行的,双方互不承担责任。
8.2 平台有权根据法律法规变化或业务运营需要,对本协议进行修订,修订后的协议将在平台公示。如号主继续使用优质号主服务,视为接受修订内容。
九、协议终止
9.1 号主可随时申请退出优质号主计划。满足信用保证金解冻条件的,200元信用保证金予以解冻退还,优质号主资格即时取消。
9.2 号主严重违反本协议或平台规则的,平台有权单方面取消其优质号主资格。信用保证金不扣除;如存在未完成订单或未解决纠纷,可继续冻结至相关事项处理完毕。
9.3 优质号主资格取消后,对应的权益(特殊曝光、包赔手续费减半、发布渠道选择、极品专区专项收益比例等)同步终止。
十、附则
10.1 本协议的解释权归大锤商行平台所有。
10.2 本协议自号主勾选同意并完成200元信用保证金冻结之日起生效。
10.3 如本协议与平台其他规则存在冲突,以本协议为准。
本人已详细阅读并充分理解本协议全部内容,同意遵守上述条款。`,
}
}
@@ -55,6 +212,15 @@ func ParseConfigValue(value string) (Rules, error) {
if rules.PremiumInsuranceFeeBps < 0 || rules.PremiumInsuranceFeeBps > rules.InsuranceFeeBps {
return Rules{}, ErrInvalidRules
}
rules.AgreementTitle = strings.TrimSpace(rules.AgreementTitle)
rules.AgreementContent = strings.TrimSpace(rules.AgreementContent)
if rules.AgreementTitle == "" || rules.AgreementContent == "" {
return Rules{}, ErrInvalidRules
}
if rules.AgreementReadSeconds < defaultAgreementReadSeconds ||
rules.AgreementReadSeconds > MaxAgreementReadSeconds {
return Rules{}, ErrInvalidRules
}
return rules, nil
}
@@ -79,6 +245,9 @@ type StatusDTO struct {
RequiredDepositCent int64 `json:"required_deposit_cent"`
InsuranceFeeBps int `json:"insurance_fee_bps"`
PremiumInsuranceFeeBps int `json:"premium_insurance_fee_bps"`
AgreementTitle string `json:"agreement_title"`
AgreementContent string `json:"agreement_content"`
AgreementReadSeconds int `json:"agreement_read_seconds"`
}
type AdminSetRequest struct {
@@ -10,6 +10,11 @@ func TestParseConfigValueDefaultsAndValidation(t *testing.T) {
if rules.DepositAmountCent != defaultDepositCent || rules.InsuranceFeeBps != 800 || rules.PremiumInsuranceFeeBps != 400 {
t.Fatalf("unexpected rules: %+v", rules)
}
if rules.AgreementTitle != defaultAgreementTitle ||
rules.AgreementReadSeconds != defaultAgreementReadSeconds ||
rules.AgreementContent == "" {
t.Fatalf("unexpected agreement defaults: %+v", rules)
}
if _, err := ParseConfigValue(`{"deposit_amount_cent":20000,"insurance_fee_bps":400,"premium_insurance_fee_bps":800}`); err == nil {
t.Fatal("expected premium rate above normal rate to be rejected")
@@ -17,6 +22,9 @@ func TestParseConfigValueDefaultsAndValidation(t *testing.T) {
if _, err := ParseConfigValue(`{"deposit_amount_cent":0}`); err == nil {
t.Fatal("expected zero deposit to be rejected")
}
if _, err := ParseConfigValue(`{"agreement_read_seconds":14}`); err == nil {
t.Fatal("expected agreement reading time below 15 seconds to be rejected")
}
}
func TestDefaultConfigValueCanBeParsed(t *testing.T) {
@@ -27,4 +35,7 @@ func TestDefaultConfigValueCanBeParsed(t *testing.T) {
if rules.DepositAmountCent != 20000 || rules.InsuranceFeeBps != 800 || rules.PremiumInsuranceFeeBps != 400 {
t.Fatalf("unexpected default rules: %+v", rules)
}
if rules.AgreementReadSeconds != 15 || rules.AgreementTitle == "" || rules.AgreementContent == "" {
t.Fatalf("unexpected default agreement: %+v", rules)
}
}
@@ -468,5 +468,8 @@ func toStatusDTO(user model.User, account model.WalletAccount, rules Rules, acti
RequiredDepositCent: rules.DepositAmountCent,
InsuranceFeeBps: rules.InsuranceFeeBps,
PremiumInsuranceFeeBps: rules.PremiumInsuranceFeeBps,
AgreementTitle: rules.AgreementTitle,
AgreementContent: rules.AgreementContent,
AgreementReadSeconds: rules.AgreementReadSeconds,
}
}
@@ -40,7 +40,7 @@ var defaultConfigs = []defaultConfig{
{Key: "order.return_overdue_grace_minutes", Value: "10", Description: "预计截止后结账宽限分钟数"},
{Key: "listing.review_required", Value: "false", Description: "发布账号是否需要后台人工审核"},
{Key: "listing.publish_cooldown_minutes", Value: "5", Description: "同一用户两次新增发布账号的最小间隔分钟数(0 表示关闭)"},
{Key: qualityowner.ConfigKey, Value: qualityowner.DefaultConfigValue(), Description: "优质号主保证金与无忧租号保险费率配置 JSON"},
{Key: qualityowner.ConfigKey, Value: qualityowner.DefaultConfigValue(), Description: "优质号主保证金、开通协议与无忧租号保险费率配置 JSON"},
{Key: listingPublishAgreementsConfigKey, Value: defaultListingPublishAgreementsConfigValue(), Description: "发布账号前协议配置 JSON"},
{Key: orderAgreementsConfigKey, Value: defaultOrderAgreementsConfigValue(), Description: "下单前协议配置 JSON"},
{Key: postRentalNoticeConfigKey, Value: defaultPostRentalNoticeConfigValue(), Description: "租后须知配置 JSON"},
@@ -134,6 +134,12 @@ func (r *Repository) List(ctx context.Context) ([]ConfigDTO, error) {
}
items := make([]ConfigDTO, 0, len(rows))
for _, row := range rows {
if row.Key == qualityowner.ConfigKey {
rules, err := qualityowner.ParseConfigValue(row.Value)
if err == nil {
row.Value, _ = qualityowner.MarshalConfig(rules)
}
}
items = append(items, toDTO(row))
}
return items, nil