MultiTenancyServer

2025-12-07 0 402

MultiTenancyServer

MultiTenancyServer目标是轻巧的软件包,可轻松地为任何代码库添加多租赁支持。它的设计受到ASP.NET核心身份的严重影响。您可以在模型中添加多种租赁支持,而无需在任何类或实体中添加任何租户密钥属性。使用ASP.NET核心,可以通过自定义域名,子域,部分主机名,HTTP请求标头,儿童或部分URL路径,查询字符串参数,经过身份验证的用户索赔或自定义请求解析器实现来检索当前租户。使用实体框架核心,将租户密钥添加为阴影属性(或可选的具体属性)并通过全局查询过滤器强制执行,所有可配置的选项都可以从默认值或每个实体中设置为超过。下面的示例突出显示了如何将MultiTenancyServer与ASP.NET核心身份和身份证4一起使用。您可以找到许多与IndentityServer4,ASP.NET Core Identity(使用不同的关键类型(例如String和INT64))以及样本repo中的实体框架核心集成的完整样本。

定义模型

定义您自己的房客模型,或从租赁者继承,或者只是使用租赁者。在此示例中,我们将从租赁者继承并添加显示名称。

 public class Tenant : TenancyTenant
{
    // Custom property for display name of tenant.
    public string Name { get ; set ; }
} 

登记服务

向ASP.NET核心添加多租赁支持的示例。

MultiTenancyServer.AspNetCore).
.AddRequestParsers(parsers =>
{
// Parsers are processed in the order they are added,
// typically 1 or 2 parsers should be all you need.
parsers
// www.tenant1.com
.AddDomainParser()
// tenant1.tenants. MultiTenancyServer .io
.AddSubdomainParser(\”.tenants. MultiTenancyServer .io\”)
// from partial hostname
.AddHostnameParser(\”^(regular_expression)$\”)
// HTTP header X-TENANT = tenant1
.AddHeaderParser(\”X-TENANT\”)
// /tenants/tenant1
.AddChildPathParser(\”/tenants/\”)
// from partial path
.AddPathParser(\”^(regular_expression)$\”)
// ?tenant=tenant1
.AddQueryParser(\”tenant\”)
// Claim from authenticated user principal.
.AddClaimParser(\”http://schemas.micr*oso**ft.com/identity/claims/tenantid\”)
// Add custom request parser with lambda.
.AddCustomParser(httpContext => \”tenant1\”);
// Add custom request parser implementation.
.AddMyCustomParser();
})
// Use in memory tenant store for development ( MultiTenancyServer .Stores)
.AddInMemoryStore(new Tenant[]
{
new Tenant()
{
Id = \”TENANT_1\”,
CanonicalName = \”Tenant1\”,
NormalizedCanonicalName = \”TENANT1\”
}
})
// Use EF Core store for production ( MultiTenancyServer .EntityFrameworkCore).
.AddEntityFrameworkStore<AppDbContext, Tenant, string>()
// Use custom store.
.AddMyCustomStore();

// Add ASP.NET Core Identity
services.AddIdentity<User, Role>()
.AddEntityFrameworkStores<AppDbContext>()
.AddDefaultTokenProviders();

// Add Identity Server 4
var builder = services.AddIdentityServer()
.AddAspNetIdentity<User>()
// Add the config data from DB (clients, resources)
.AddConfigurationStore<AppDbContext>(options =>
{
options.ConfigureDbContext = b =>
b.UseSqlServer(connectionString,
sql => sql.MigrationsAssembly(migrationsAssemblyName));
})
// Add the operational data from DB (codes, tokens, consents)
.AddOperationalStore<AppDbContext>(options =>
{
options.ConfigureDbContext = b =>
b.UseSqlServer(connectionString,
sql => sql.MigrationsAssembly(migrationsAssemblyName));
});

if (Environment.IsDevelopment())
{
builder.AddDeveloperSigningCredential();
}
else
{
throw new Exception(\”Key not configured.\”);
}
} \”>

 public void ConfigureServices ( IServiceCollection services )
{
    var connectionString = Configuration . GetConnectionString ( \"DefaultConnection\" ) ;
    var migrationsAssembly = typeof ( AppDbContext ) . GetTypeInfo ( ) . Assembly . GetName ( ) . Name ;

    services . AddDbContext < AppDbContext > ( options =>
    {
        options . UseSqlServer ( connectionString , sql => sql . MigrationsAssembly ( migrationsAssembly ) ) ;
    } ) ;

    // Add Multi-Tenancy Server defining TTenant<TKey> as type Tenant with an ID (key) of type string.
    services . AddMultiTenancy < Tenant , string > ( )
        // Add one or more IRequestParser ( MultiTenancyServer .AspNetCore).
        . AddRequestParsers ( parsers =>
        {
            // Parsers are processed in the order they are added,
            // typically 1 or 2 parsers should be all you need.
            parsers
                // www.tenant1.com
                . AddDomainParser ( )
                // tenant1.tenants. MultiTenancyServer .io
                . AddSubdomainParser ( \".tenants. MultiTenancyServer .io\" )
                // from partial hostname
                . AddHostnameParser ( \"^(regular_expression)$\" )
                // HTTP header X-TENANT = tenant1
                . AddHeaderParser ( \"X-TENANT\" )
                // /tenants/tenant1
                . AddChildPathParser ( \"/tenants/\" )
                // from partial path
                . AddPathParser ( \"^(regular_expression)$\" )
                // ?tenant=tenant1
                . AddQueryParser ( \"tenant\" )
                // Claim from authenticated user principal.
                . AddClaimParser ( \"http://schemas.micr*oso**ft.com/identity/claims/tenantid\" )
                // Add custom request parser with lambda.
                . AddCustomParser ( httpContext => \"tenant1\" ) ;
                // Add custom request parser implementation.
                . AddMyCustomParser ( ) ;
        } )
        // Use in memory tenant store for development ( MultiTenancyServer .Stores)
        . AddInMemoryStore ( new Tenant [ ] 
        { 
            new Tenant ( ) 
            { 
                Id = \"TENANT_1\" , 
                CanonicalName = \"Tenant1\" , 
                NormalizedCanonicalName = \"TENANT1\"
            }
        } )
        // Use EF Core store for production ( MultiTenancyServer .EntityFrameworkCore).
        . AddEntityFrameworkStore < AppDbContext , Tenant , string > ( )
        // Use custom store.
        . AddMyCustomStore ( ) ;
        
    // Add ASP.NET Core Identity
    services . AddIdentity < User , Role > ( )
        . AddEntityFrameworkStores < AppDbContext > ( )
        . AddDefaultTokenProviders ( ) ;

    // Add Identity Server 4
    var builder = services . AddIdentityServer ( )
        . AddAspNetIdentity < User > ( )
        // Add the config data from DB (clients, resources)
        . AddConfigurationStore < AppDbContext > ( options =>
        {
            options . ConfigureDbContext = b =>
                b . UseSqlServer ( connectionString ,
                    sql => sql . MigrationsAssembly ( migrationsAssemblyName ) ) ;
        } )
        // Add the operational data from DB (codes, tokens, consents)
        . AddOperationalStore < AppDbContext > ( options =>
        {
            options . ConfigureDbContext = b =>
                b . UseSqlServer ( connectionString ,
                    sql => sql . MigrationsAssembly ( migrationsAssemblyName ) ) ;
        } ) ;

    if ( Environment . IsDevelopment ( ) )
    {
        builder . AddDeveloperSigningCredential ( ) ;
    }
    else
    {
        throw new Exception ( \"Key not configured.\" ) ;
    }
}    

添加中间件

为ASP.NET Core MVC和IdentityServer4配置应用程序的示例。

 public void Configure ( IApplicationBuilder app )
{
    // other code removed for brevity

    app . UseStaticFiles ( ) ;
    app . UseMultiTenancy < Tenant > ( ) ;
    app . UseIdentityServer ( ) ;
    app . UseAuthentication ( ) ;
    app . UseMvcWithDefaultRoute ( ) ;
} 

配置实体

dbContext的示例具有多种租赁支持对ASP.NET核心身份和身份服务员4。

MultiTenancyServer EF Core
ITenantDbContext<Tenant, string>
{
private static object _tenancyModelState;
private readonly ITenancyContext<Tenant> _tenancyContext;

public AppDbContext(
DbContextOptions<AppDbContext> options,
ITenancyContext<Tenant> tenancyContext)
: base(options)
{
// The request scoped tenancy context.
// Should not access the tenancyContext.Tenant property in the constructor yet,
// as the request pipeline has not finished running yet and it will likely be null.
_tenancyContext = tenancyContext;
}

// IdentityServer4 implementation.
public DbSet<Client> Clients { get; set; }
public DbSet<IdentityResource> IdentityResources { get; set; }
public DbSet<ApiResource> ApiResources { get; set; }
public DbSet<PersistedGrant> PersistedGrants { get; set; }

// MultiTenancyServer implementation.
public DbSet<Tenant> Tenants { get; set; }

protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);

// IdentityServer4 configuration.
var configurationStoreOptions = new ConfigurationStoreOptions();
builder.ConfigureClientContext(configurationStoreOptions);
builder.ConfigureResourcesContext(configurationStoreOptions);
var operationalStoreOptions = new OperationalStoreOptions();
builder.ConfigurePersistedGrantContext(operationalStoreOptions);

// MultiTenancyServer configuration.
var tenantStoreOptions = new TenantStoreOptions();
builder.ConfigureTenantContext<Tenant, string>(tenantStoreOptions);

// Add multi-tenancy support to model.
var tenantReferenceOptions = new TenantReferenceOptions();
builder.HasTenancy<string>(tenantReferenceOptions, out _tenancyModelState);

// Configure custom properties on Tenant ( MultiTenancyServer ).
builder.Entity<Tenant>(b =>
{
b.Property(t => t.Name).HasMaxLength(256);
});

// Configure properties on User (ASP.NET Core Identity).
builder.Entity<User>(b =>
{
// Add multi-tenancy support to entity.
b.HasTenancy(() => _tenantId, _tenancyModelState, hasIndex: false);
// Remove unique index on NormalizedUserName.
b.HasIndex(u => u.NormalizedUserName).HasName(\”UserNameIndex\”).IsUnique(false);
// Add unique index on TenantId and NormalizedUserName.
b.HasIndex(tenantReferenceOptions.ReferenceName, nameof(User.NormalizedUserName))
.HasName(\”TenantUserNameIndex\”).IsUnique();
});

// Configure properties on Role (ASP.NET Core Identity).
builder.Entity<Role>(b =>
{
// Add multi-tenancy support to entity.
b.HasTenancy(() => _tenancyContext.Tenant.Id, _tenancyModelState, hasIndex: false);
// Remove unique index on NormalizedName.
b.HasIndex(r => r.NormalizedName).HasName(\”RoleNameIndex\”).IsUnique(false);
// Add unique index on TenantId and NormalizedName.
b.HasIndex(tenantReferenceOptions.ReferenceName, nameof(Role.NormalizedName))
.HasName(\”TenantRoleNameIndex\”).IsUnique();
});

// Configure properties on Client (IdentityServer4).
builder.Entity<Client>(b =>
{
// Add multi-tenancy support to entity.
b.HasTenancy(() => _tenancyContext.Tenant.Id, _tenancyModelState, hasIndex: false);
// Remove unique index on ClientId.
b.HasIndex(c => c.ClientId).IsUnique(false);
// Add unique index on TenantId and ClientId.
b.HasIndex(tenantReferenceOptions.ReferenceName, nameof(Client.ClientId)).IsUnique();
});

// Configure properties on IdentityResource (IdentityServer4).
builder.Entity<IdentityResource>(b =>
{
// Add multi-tenancy support to entity.
b.HasTenancy(() => _tenancyContext.Tenant.Id, _tenancyModelState, hasIndex: false);
// Remove unique index on Name.
b.HasIndex(r => r.Name).IsUnique(false);
// Add unique index on TenantId and Name.
b.HasIndex(tenantReferenceOptions.ReferenceName, nameof(IdentityResource.Name)).IsUnique();
});

// Configure properties on ApiResource (IdentityServer4).
builder.Entity<ApiResource>(b =>
{
// Add multi-tenancy support to entity.
b.HasTenancy(() => _tenancyContext.Tenant.Id, _tenancyModelState, hasIndex: false);
// Remove unique index on Name.
b.HasIndex(r => r.Name).IsUnique(false);
// Add unique index on TenantId and Name.
b.HasIndex(tenantReferenceOptions.ReferenceName, nameof(ApiResource.Name)).IsUnique();
});

// Configure properties on ApiScope (IdentityServer4).
builder.Entity<ApiScope>(b =>
{
// Add multi-tenancy support to entity.
b.HasTenancy(() => _tenancyContext.Tenant.Id, _tenancyModelState, hasIndex: false);
// Remove unique index on Name.
b.HasIndex(s => s.Name).IsUnique(false);
// Add unique index on TenantId and Name.
b.HasIndex(tenantReferenceOptions.ReferenceName, nameof(ApiScope.Name)).IsUnique();
});

// Configure properties on PersistedGrant (IdentityServer4).
builder.Entity<PersistedGrant>(b =>
{
// Add multi-tenancy support to entity.
b.HasTenancy(() => _tenancyContext.Tenant.Id, _tenancyModelState);
});
}

public override int SaveChanges(bool acceptAllChangesOnSuccess)
{
// Ensure multi-tenancy for all tenantable entities.
this.EnsureTenancy(_tenancyContext?.Tenant?.Id, _tenancyModelState, _logger);
return base.SaveChanges(acceptAllChangesOnSuccess);
}

public override Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default)
{
// Ensure multi-tenancy for all tenantable entities.
this.EnsureTenancy(_tenancyContext?.Tenant?.Id, _tenancyModelState, _logger);
return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
}
}\”>

    public class AppDbContext : 
        // ASP.NET Core Identity EF Core
        IdentityDbContext < User , Role , string , UserClaim , UserRole , UserLogin , RoleClaim , UserToken > , 
        // IdentityServer4 EF Core
        IConfigurationDbContext , IPersistedGrantDbContext ,
        // MultiTenancyServer EF Core
        ITenantDbContext < Tenant , string >
    {
        private static object _tenancyModelState ;
        private readonly ITenancyContext < Tenant > _tenancyContext ;

        public AppDbContext (
            DbContextOptions < AppDbContext > options , 
            ITenancyContext < Tenant > tenancyContext )
            : base ( options )
        {
            // The request scoped tenancy context.
            // Should not access the tenancyContext.Tenant property in the constructor yet,
            // as the request pipeline has not finished running yet and it will likely be null.
            _tenancyContext = tenancyContext ;
        }

        // IdentityServer4 implementation.
        public DbSet < Client > Clients { get ; set ; }
        public DbSet < IdentityResource > IdentityResources { get ; set ; }
        public DbSet < ApiResource > ApiResources { get ; set ; }
        public DbSet < PersistedGrant > PersistedGrants { get ; set ; }

        // MultiTenancyServer implementation.
        public DbSet < Tenant > Tenants { get ; set ; }

        protected override void OnModelCreating ( ModelBuilder builder )
        {
            base . OnModelCreating ( builder ) ;

            // IdentityServer4 configuration.
            var configurationStoreOptions = new ConfigurationStoreOptions ( ) ;
            builder . ConfigureClientContext ( configurationStoreOptions ) ;
            builder . ConfigureResourcesContext ( configurationStoreOptions ) ;
            var operationalStoreOptions = new OperationalStoreOptions ( ) ;
            builder . ConfigurePersistedGrantContext ( operationalStoreOptions ) ;

            // MultiTenancyServer configuration.
            var tenantStoreOptions = new TenantStoreOptions ( ) ;
            builder . ConfigureTenantContext < Tenant , string > ( tenantStoreOptions ) ;

            // Add multi-tenancy support to model.
            var tenantReferenceOptions = new TenantReferenceOptions ( ) ;
            builder . HasTenancy < string > ( tenantReferenceOptions , out _tenancyModelState ) ;

            // Configure custom properties on Tenant ( MultiTenancyServer ).
            builder . Entity < Tenant > ( b =>
            {
                b . Property ( t => t . Name ) . HasMaxLength ( 256 ) ;
            } ) ;

            // Configure properties on User (ASP.NET Core Identity).
            builder . Entity < User > ( b =>
            {
                // Add multi-tenancy support to entity.
                b . HasTenancy ( ( ) => _tenantId , _tenancyModelState , hasIndex : false ) ;
                // Remove unique index on NormalizedUserName.
                b . HasIndex ( u => u . NormalizedUserName ) . HasName ( \"UserNameIndex\" ) . IsUnique ( false ) ;
                // Add unique index on TenantId and NormalizedUserName.
                b . HasIndex ( tenantReferenceOptions . ReferenceName , nameof ( User . NormalizedUserName ) )
                    . HasName ( \"TenantUserNameIndex\" ) . IsUnique ( ) ;
            } ) ;

            // Configure properties on Role (ASP.NET Core Identity).
            builder . Entity < Role > ( b =>
            {
                // Add multi-tenancy support to entity.
                b . HasTenancy ( ( ) => _tenancyContext . Tenant . Id , _tenancyModelState , hasIndex : false ) ;
                // Remove unique index on NormalizedName.
                b . HasIndex ( r => r . NormalizedName ) . HasName ( \"RoleNameIndex\" ) . IsUnique ( false ) ;
                // Add unique index on TenantId and NormalizedName.
                b . HasIndex ( tenantReferenceOptions . ReferenceName , nameof ( Role . NormalizedName ) )
                    . HasName ( \"TenantRoleNameIndex\" ) . IsUnique ( ) ;
            } ) ;

            // Configure properties on Client (IdentityServer4).
            builder . Entity < Client > ( b =>
            {
                // Add multi-tenancy support to entity.
                b . HasTenancy ( ( ) => _tenancyContext . Tenant . Id , _tenancyModelState , hasIndex : false ) ;
                // Remove unique index on ClientId.
                b . HasIndex ( c => c . ClientId ) . IsUnique ( false ) ;
                // Add unique index on TenantId and ClientId.
                b . HasIndex ( tenantReferenceOptions . ReferenceName , nameof ( Client . ClientId ) ) . IsUnique ( ) ;
            } ) ;

            // Configure properties on IdentityResource (IdentityServer4).
            builder . Entity < IdentityResource > ( b =>
            {
                // Add multi-tenancy support to entity.
                b . HasTenancy ( ( ) => _tenancyContext . Tenant . Id , _tenancyModelState , hasIndex : false ) ;
                // Remove unique index on Name.
                b . HasIndex ( r => r . Name ) . IsUnique ( false ) ;
                // Add unique index on TenantId and Name.
                b . HasIndex ( tenantReferenceOptions . ReferenceName , nameof ( IdentityResource . Name ) ) . IsUnique ( ) ;
            } ) ;

            // Configure properties on ApiResource (IdentityServer4).
            builder . Entity < ApiResource > ( b =>
            {
                // Add multi-tenancy support to entity.
                b . HasTenancy ( ( ) => _tenancyContext . Tenant . Id , _tenancyModelState , hasIndex : false ) ;
                // Remove unique index on Name.
                b . HasIndex ( r => r . Name ) . IsUnique ( false ) ;
                // Add unique index on TenantId and Name.
                b . HasIndex ( tenantReferenceOptions . ReferenceName , nameof ( ApiResource . Name ) ) . IsUnique ( ) ;
            } ) ;

            // Configure properties on ApiScope (IdentityServer4).
            builder . Entity < ApiScope > ( b =>
            {
                // Add multi-tenancy support to entity.
                b . HasTenancy ( ( ) => _tenancyContext . Tenant . Id , _tenancyModelState , hasIndex : false ) ;
                // Remove unique index on Name.
                b . HasIndex ( s => s . Name ) . IsUnique ( false ) ;
                // Add unique index on TenantId and Name.
                b . HasIndex ( tenantReferenceOptions . ReferenceName , nameof ( ApiScope . Name ) ) . IsUnique ( ) ;
            } ) ;

            // Configure properties on PersistedGrant (IdentityServer4).
            builder . Entity < PersistedGrant > ( b =>
            {
                // Add multi-tenancy support to entity.
                b . HasTenancy ( ( ) => _tenancyContext . Tenant . Id , _tenancyModelState ) ;
            } ) ;
        }

        public override int SaveChanges ( bool acceptAllChangesOnSuccess )
        {
            // Ensure multi-tenancy for all tenantable entities.
            this . EnsureTenancy ( _tenancyContext ? . Tenant ? . Id , _tenancyModelState , _logger ) ;
            return base . SaveChanges ( acceptAllChangesOnSuccess ) ;
        }

        public override Task < int > SaveChangesAsync ( bool acceptAllChangesOnSuccess , CancellationToken cancellationToken = default )
        {
            // Ensure multi-tenancy for all tenantable entities.
            this . EnsureTenancy ( _tenancyContext ? . Tenant ? . Id , _tenancyModelState , _logger ) ;
            return base . SaveChangesAsync ( acceptAllChangesOnSuccess , cancellationToken ) ;
        }
    } 

其他选项

 public class TenantReferenceOptions
{
    // Summary:
    //     If set to a non-null value, the store will use this value as the name for the
    //     tenant\'s reference property. The default is \"TenantId\".
    public string ReferenceName { get ; set ; }

    // Summary:
    //     True to enable indexing of tenant reference properties in the store, otherwise
    //     false. The default is true.
    public bool IndexReferences { get ; set ; }

    // Summary:
    //     If set to a non-null value, the store will use this value as the name of the
    //     index for any tenant references. The name is also a format pattern of {0:PropertyName}.
    //     The default is \"{0}Index\", eg. \"TenantIdIndex\".
    public string IndexNameFormat { get ; set ; }

    // Summary:
    //     Determines if a null tenant reference is allowed for entities and how querying
    //     for null tenant references is handled.
    public NullTenantReferenceHandling NullTenantReferenceHandling { get ; set ; }
}

public enum NullTenantReferenceHandling
{
    // Summary:
    //     A null tenant reference is NOT allowed for the entity, where possible a NOT NULL
    //     or REQUIRED constraint should be set on the tenant reference, querying for entities
    //     with a null tenant reference will match NO entities.
    //     This is the default option.
    NotNullDenyAccess = 0 ,

    // Summary:
    //     A null tenant reference is allowed for the entity, where possible an NULLABLE
    //     or OPTIONAL constraint should be set on the tenant reference, querying for entities
    //     with a null tenant reference will match those expected results.
    //     This may be useful where globally defined system entities are set with a null
    //     tenant reference.

下载源码

通过命令行克隆项目:

git clone https://github.com/MultiTenancyServer/MultiTenancyServer.git

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

申明:本文由第三方发布,内容仅代表作者观点,与本网站无关。对本文以及其中全部或者部分内容的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。本网发布或转载文章出于传递更多信息之目的,并不意味着赞同其观点或证实其描述,也不代表本网对其真实性负责。

左子网 开发教程 MultiTenancyServer https://www.zuozi.net/31832.html

phpGPX
上一篇: phpGPX
WebServer
下一篇: WebServer
常见问题
  • 1、自动:拍下后,点击(下载)链接即可下载;2、手动:拍下后,联系卖家发放即可或者联系官方找开发者发货。
查看详情
  • 1、源码默认交易周期:手动发货商品为1-3天,并且用户付款金额将会进入平台担保直到交易完成或者3-7天即可发放,如遇纠纷无限期延长收款金额直至纠纷解决或者退款!;
查看详情
  • 1、描述:源码描述(含标题)与实际源码不一致的(例:货不对板); 2、演示:有演示站时,与实际源码小于95%一致的(但描述中有”不保证完全一样、有变化的可能性”类似显著声明的除外); 3、发货:不发货可无理由退款; 4、安装:免费提供安装服务的源码但卖家不履行的; 5、收费:价格虚标,额外收取其他费用的(但描述中有显著声明或双方交易前有商定的除外); 6、其他:如质量方面的硬性常规问题BUG等。 注:经核实符合上述任一,均支持退款,但卖家予以积极解决问题则除外。
查看详情
  • 1、左子会对双方交易的过程及交易商品的快照进行永久存档,以确保交易的真实、有效、安全! 2、左子无法对如“永久包更新”、“永久技术支持”等类似交易之后的商家承诺做担保,请买家自行鉴别; 3、在源码同时有网站演示与图片演示,且站演与图演不一致时,默认按图演作为纠纷评判依据(特别声明或有商定除外); 4、在没有”无任何正当退款依据”的前提下,商品写有”一旦售出,概不支持退款”等类似的声明,视为无效声明; 5、在未拍下前,双方在QQ上所商定的交易内容,亦可成为纠纷评判依据(商定与描述冲突时,商定为准); 6、因聊天记录可作为纠纷评判依据,故双方联系时,只与对方在左子上所留的QQ、手机号沟通,以防对方不承认自我承诺。 7、虽然交易产生纠纷的几率很小,但一定要保留如聊天记录、手机短信等这样的重要信息,以防产生纠纷时便于左子介入快速处理。
查看详情

相关文章

猜你喜欢
发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务