沐雪.NetCore多租户商城系统多商户的小程序商城系统

时间:2024-02-21 15:31:48

历经1年多,沐雪.NetCore版本的多租户商城系统终于研发成功。全新的技术,全新的界面!我们将采用新的产品名称 -- 多租宝,并且以后都将全部精力致力于该平台的升级研发。

《多租宝功能简介

SaaS模式的多租户/多商户小程序商城系统,可以同时支持多个租户/商户, 每个商户又可以创建多个店铺,每个店铺对应一个小程序商城系统,各个店铺管理各自的店铺功能。

自带平台管理系统,可以查看和管理所有商户和所有店铺信息,可以给商户的店铺充值续费,可对所有店铺里的商品和订单进行管理,以及数据统计。

 官网地址:http://www.duozubao.cn/

一、平台方管理系统

1、平台概览:今日新增租户数,今日新增店铺数,今日付款金额,待处理事项(待付款订单,待发货订单,待续费店铺数等),商城信息统计(会员总数,商品总数,订单总数等)

2、租户管理:租户账号管理,租户店铺管理,租户店铺续费,店铺禁用,店铺资料修改等;

3、所有店铺的商品信息管理;

4、所有店铺订单管理;

5、所有店铺的会员管理;

6、所有店铺的交易流水记录;

7、系统功能:站点设置(短信设置,交易设置,租户设置等), 管理员管理, 菜单设置, 权限设置,消息模板设置,自动代码生成器

 

二、租户/商户平台

支持商户自己注册账号,自行创建店铺,试用店铺里的功能;

 

1、租户注册账号,登录,修改密码/找回密码,修改资料等;

2、免费创建店铺,管理店铺;

3、店铺概览:今日新增会员数,今日订单数,今日付款金额,交易统计,快捷入口

    待处理事项(商品库存报警,待付款订单,备货中订单,待待收货订单,待回复评价,仓库中商品),

    商城信息统计(会员总数,商品总,近七天评价数,近七天订单数,近7天销售额(元)),

4、店铺管理:信息设置,小程序设置,意见反馈

5、图片库:图片分类,图片上传,图片管理;

6、商品管理:商品发布,分组,标签,规格,评价,商品管理等;

7、订单管理:订单管理(所有订单,待付款,待发货,已完成),发货,批量发货,备注,订单详细查看,物流信息,运费模板,交易设置等;

8、会员管理:会员查看,会员下单记录;

9、店铺的交易流水;

10、营销管理:优惠券,满额减;

11、广告管理,文章管理;

 

三、小程序端

 

1、手机号注册,微信号授权登录,修改资料,上传头像等;

2、首页广告位,banner,商品列表,商品分类,商品查询,商品详情,电话客服

3、优惠券领取,满额减;

4、商品加入购物车,购物车管理;

5、商品收藏,收藏管理;

6、下单,创建订单,选择收货地址,选择支付方式(支持微信支付和货到付款),微信支付;

7、用户中心:订单概览,我的优惠券列表,订单管理(查看,取消订单,订单列表,订单详情,确认收货,物流信息,订单重新付款,删除订单,评价,联系客服),商品浏览历史,地址管理,我的收藏,意见反馈,设置

 

《多租宝》技术概览:

.NetCore是大势所趋,是时候技术升级了。我们的系统不仅仅是一套成熟的多租户商城系统源码!

购买我们的源码,您将很容易掌握所有常用的.NetCore的技术框架,组件和开发思想,以及微服务架构的搭建。

我们的源码,会从.NetCore3.1---> .Net 6 ---> ....一直不断升级技术架构;

我们的源码,会从SaaS商城的常用功能,逐渐完美,强大,更加通用,最终做成一艘技术框架的航母,可以承载更多的功能。

 

后端开发语言:.Net 6

数据库:mysql 5.7

小程序前端:uni-app,跨平台

后台:asp.net core 6 mvc  +bootstrap4

接口:.Net6 webapi

Job: HangFire

缓存:Redis

ORM:采用EFCore ,集成 Helper类

DDD领域驱动设计;

DI:采用微软自带的容器,部分采用Autoface;大量使用依赖注入;

自带代码生长器,免去写实体层,仓储层,和单表的增删改查等功能;

全异步方式执行,高性能,易扩展;全局日志跟踪,异步捕获;

接口采用JWT鉴权,安全有保障;

短信通知:阿里云短信通道;

SaaS模式的多租户商城系统,各店铺完全隔离,从数据库层面开始隔离;

app小程序端可以支持多平台的小程序,微信小程序,支付宝小程序,百度小程序,qq小程序,ios,Andriod等等,目前微信小程序已经全面测试通过,其他端待测试。

后端代码和接口,可以部署在windows服务器和Linux服务器,支持Docker部署。

 

租户的测试地址: http://shop.duozubao.cn/Login 可以自行注册账号,创建店铺使用。

---------------------------------------------------------------------------------------------------

欢迎前来购买整套源码!

购买后全部代码都是源码形式。

购买后获得授权,可以用于商业用途。

购买源码,另外包括1年的免费升级和1年的售后服务,免费安装一次。

赠送一套精美的asp.net core mvc 3.1版本的后台管理系统。

 

购买请联系官方qq: 23002807

手机(微信):13816330315

 -------------------------------------------------------------------------------------------------------

 

小程序段界面:

  

(可以扫码二维码查看)

 

   

超级管理员后台界面:

 

 

 

Tenant租户后台:

 

 

 

 

 

 

代码展示:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using MuXue.Extensions.DependencyInjection;
using MuXue.WeTao.Mall.Domain.Model;
using MuXue.WeTao.Mall.Repository;
using MuXue.WeTao.Mall.Service.WxOpenMessageHandler;
using MuXue.WeTao.Mall.WebApi.Extensions;
using MuXue.WeTao.Mall.WebApi.Filter; 
using Senparc.CO2NET;
using Senparc.CO2NET.AspNet;
using Senparc.NeuChar.MessageHandlers;
using Senparc.Weixin;
using Senparc.Weixin.Cache.Redis;
using Senparc.Weixin.Entities;
using Senparc.Weixin.WxOpen.MessageHandlers.Middleware;
using StackExchange.Redis.Extensions.Newtonsoft;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace MuXue.WeTao.Mall.WebApi
{
    /// <summary>
    /// 沐雪-多租宝StartUp类
    /// </summary>
    public class Startup
    {
        private const string ApiName = "MuXue.WeTao.Mall";
        readonly string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

        /// <summary>
        /// 运行环境
        /// </summary>
        public IWebHostEnvironment Env { get; set; }
        /// <summary>
        /// 配置
        /// </summary>
        public IConfiguration Configuration { get; }

        public static readonly ILoggerFactory MyLoggerFactory
                                        = LoggerFactory.Create(builder =>
                                        {
                                        #if DEBUG
                                            builder.AddConsole();
                                        #endif
                                        });

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="configuration"></param>
        /// <param name="env"></param>
        public Startup(IConfiguration configuration, IWebHostEnvironment env)
        {
            Configuration = configuration;
            Env = env;

        }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMuXueMVC(Env)
              // .AddMuXueDbContext(Configuration)
              .AddInjection() 
              .AddSwagger()
              .AddRedisExtensions<NewtonsoftSerializer>(Configuration.GetSection("Redis"))
              .AddCors(options =>
              {
                  options.AddPolicy(MyAllowSpecificOrigins,

                  builder => builder.AllowAnyOrigin()
                  // .AllowAnyHeader() 
                  //   .AllowCredentials() 
                  .WithMethods("GET", "POST", "HEAD", "PUT", "DELETE", "OPTIONS")
                  .WithHeaders("x-requested-with", "Authorization", "token", "content-type")

                  );
              })
              .AddWeixin(Configuration)
              .AddControllers();

            //配置jwt
            services.ConfigureJwt(Configuration);
            //注入JWT配置文件
           // services.Configure<JwtConfig>(Configuration.GetSection("JwtConfig"));

            // 添加mysql的dbcontext上下文
            services.AddDbContextPool<IDbContext, ApplicationDbContext>(options =>
            {
                options.UseMySql(Configuration.GetConnectionString("Default"), sqlOptions =>
                {
                    sqlOptions.EnableRetryOnFailure(3, TimeSpan.FromSeconds(30), new[] { 2 });
                }).UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking).UseLoggerFactory(MyLoggerFactory);
            }, 128);

            services.AddHttpClient();
            services.Configure<ApiBehaviorOptions>(opts => opts.SuppressModelStateInvalidFilter = true);
            services.AddControllers(options =>
            {
                options.Filters.Add<LogstashFilter>();
                options.Filters.Add<PrivilegeFilter>();
                options.Filters.Add<XcActionFilter>();
                options.Filters.Add<GlobalExceptions>();
            }).AddNewtonsoftJson(option =>
            {
                option.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
                option.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore;
            });
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IOptions<SenparcSetting> senparcSetting, IOptions<SenparcWeixinSetting> senparcWeixinSetting)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            string OpenSwagger = Configuration.GetSection("AppSettingConfig:OpenSwagger").Value;//是否开启swagger
            if (OpenSwagger == "1")
            {
                app.UseSwagger().UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", $"{ApiName} V1"); });
            }
            app.UseRouting();
            app.UseCors(MyAllowSpecificOrigins);


            // 启动 CO2NET 全局注册,必须!
            // 关于 UseSenparcGlobal() 的更多用法见 CO2NET Demo:https://github.com/Senparc/Senparc.CO2NET/blob/master/Sample/Senparc.CO2NET.Sample.netcore3/Startup.cs
            var registerService = app.UseSenparcGlobal(env, senparcSetting.Value, globalRegister =>
            {
                #region CO2NET 全局配置

                #region 全局缓存配置(按需)配置和使用 Redis          

                if (UseRedis(senparcSetting.Value, out string redisConfigurationStr))//这里为了方便不同环境的开发者进行配置,做成了判断的方式,实际开发环境一般是确定的,这里的if条件可以忽略
                {
                    Senparc.CO2NET.Cache.Redis.Register.SetConfigurationOption(redisConfigurationStr);
                    Senparc.CO2NET.Cache.Redis.Register.UseKeyValueRedisNow();//键值对缓存策略(推荐)

                }
                #endregion

                #region 注册日志(按需,建议) 
                globalRegister.RegisterTraceLog(ConfigTraceLog);//配置TraceLog 
                #endregion

                #region APM 系统运行状态统计记录配置

                //测试APM缓存过期时间(默认情况下可以不用设置)
                Senparc.CO2NET.APM.Config.EnableAPM = true;//默认已经为开启,如果需要关闭,则设置为 false
                Senparc.CO2NET.APM.Config.DataExpire = TimeSpan.FromMinutes(60);

                #endregion

                #endregion
            }, true)
                .UseSenparcWeixin(senparcWeixinSetting.Value, weixinRegister =>
            {
                if (UseRedis(senparcSetting.Value, out _))
                { 
                    weixinRegister.UseSenparcWeixinCacheRedis();//StackExchange.Redis 
                }
            })
                ;

            //使用 小程序 MessageHandler 中间件                                                   // -- DPBMARK MiniProgram
            app.UseMessageHandlerForWxOpen("/WxOpenAsync", CustomWxOpenMessageHandler.GenerateMessageHandler, options =>
            {
                options.DefaultMessageHandlerAsyncEvent = DefaultMessageHandlerAsyncEvent.SelfSynicMethod;
                options.AccountSettingFunc = context => senparcWeixinSetting.Value;
            }
            );


            app.UseAuthentication();
            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }

        // -- DPBMARK Redis
        /// <summary>
        /// 判断当前配置是否满足使用 Redis(根据是否已经修改了默认配置字符串判断)
        /// </summary>
        /// <param name="senparcSetting"></param>
        /// <param name="redisConfigurationStr">redis地址</param>
        /// <returns></returns>
        private bool UseRedis(SenparcSetting senparcSetting, out string redisConfigurationStr)
        {
            redisConfigurationStr = senparcSetting.Cache_Redis_Configuration;
            var useRedis = !string.IsNullOrEmpty(redisConfigurationStr) && redisConfigurationStr != "#{Cache_Redis_Configuration}#"/*默认值,不启用*/;
            return useRedis;
        }
        /// <summary>
        /// 配置微信跟踪日志(演示,按需)
        /// </summary>
        private void ConfigTraceLog()
        {
            //这里设为Debug状态时,/App_Data/WeixinTraceLog/目录下会生成日志文件记录所有的API请求日志,正式发布版本建议关闭

            //如果全局的IsDebug(Senparc.CO2NET.Config.IsDebug)为false,此处可以单独设置true,否则自动为true
            Senparc.CO2NET.Trace.SenparcTrace.SendCustomLog("系统日志", "系统启动");//只在Senparc.Weixin.Config.IsDebug = true的情况下生效

            //全局自定义日志记录回调
            Senparc.CO2NET.Trace.SenparcTrace.OnLogFunc = () =>
            {
                //加入每次触发Log后需要执行的代码
            };

            
        }


    }
}
View Code
using MuXue.WeTao.Mall.IRepository;
using MuXue.WeTao.Mall.Repository;
using System;
using System.Collections.Generic;
using Microsoft.Extensions.Logging;
using System.Threading.Tasks;
using MuXue.WeTao.Mall.Domain.Model;
using MuXue.WeTao.Mall.Domain.Model.Request;
using MuXue.WeTao.Mall.Domain.Entity;
using MuXue.WeTao.Mall.Common;
using MuXue.WeTao.Mall.Domain.Model.response;
using System.Linq;
using System.Linq.Expressions;
using MuXue.WeTao.Mall.Domain.Model.Common;
using System.Transactions;
using Microsoft.AspNetCore.Mvc.Formatters;
using System.Text;
using MySql.Data.MySqlClient;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
using MuXue.WeTao.Mall.Domain.Model.MXEnum;
using Senparc.Weixin.TenPay.V3;
using static MuXue.WeTao.Mall.Domain.Model.MXEnum.TenantOrderSomeEnum;
using Senparc.Weixin.TenPay;
using MuXue.WeTao.Mall.Core.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using System.Text.Encodings.Web;
using System.Web;
using Senparc.CO2NET.Extensions;

namespace MuXue.WeTao.Mall.Service.Impl
{
    public class PaymentService : IPaymentService
    {
        private ILogger _logger;
        private IDbContext _dbContext;
        private IUnitOfWork _unitOfWork;
        private IConfiguration _config;

        private Itenant_orderRepository _orderRepository;
        private Itenant_order_itemRepository _order_itemRepository;

        private IMemberService _memberService;
        private ITenantGoodsService _goodsService;

        private ITenantShopBaseService _shopBaseService;
        private IWxOpenUserInfoServie _wxOpenUserInfoServie;
        private ITenantAppBaseinfoService _appBaseinfoService;

        public PaymentService(IDbContext dbContext, ILogger<PaymentService> logger, IConfiguration config, IMemberService memberService,
            ITenantShopBaseService shopBaseService, IWxOpenUserInfoServie wxOpenUserInfoServie, ITenantAppBaseinfoService appBaseinfoService)
        {
            _dbContext = dbContext;
            _unitOfWork = new UnitOfWork(_dbContext);
            _config = config;

            _orderRepository = new tenant_orderRepository(_dbContext);
            _order_itemRepository = new tenant_order_itemRepository(_dbContext);

            _logger = logger;
            _memberService = memberService;

            _shopBaseService = shopBaseService;
            _wxOpenUserInfoServie = wxOpenUserInfoServie;
            _appBaseinfoService = appBaseinfoService;
        }


        /// <summary>
        /// 订单查询
        /// </summary>
        /// <param name="shop_code"></param>
        /// <param name="appBaseInfo">可以为null</param>
        /// <param name="transaction_id"></param>
        /// <param name="out_trade_no"></param>
        /// <returns></returns>
        public async Task<OrderQueryResult> OrderQuery(string shop_code, tenant_app_baseinfo appBaseInfo, string transaction_id, string out_trade_no)
        {

            try
            {

                if (appBaseInfo == null)
                {
                    appBaseInfo = new tenant_app_baseinfo();
                    appBaseInfo = await _appBaseinfoService.GetModel(shop_code, AppChannelEnum.wxminapp);
                }

                string nonceStr = TenPayV3Util.GetNoncestr();

                TenPayV3OrderQueryRequestData queryData = new TenPayV3OrderQueryRequestData(appBaseInfo.AppId, appBaseInfo.MchId,
                    transaction_id, nonceStr, out_trade_no, appBaseInfo.PayKey32);

                OrderQueryResult result = await TenPayV3.OrderQueryAsync(queryData);

                return result;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"查询订单异常:{ex.Message}");
                return null;
            }
        }
    }
}
View Code
/**
*┌──────────────────────────────────────────────────────────────┐
*│ 描    述:沐雪-多租宝-快递100物流信息查询帮助类                                                   
*│ 作    者:多租户商城系统                                           
*│ 版    本:muxue.wetao.saas.v1 沐雪微信商城系列产品-模板代码自动生成 
*│ 创建时间:2020-10-01 16:46:46
*│  上海沐雪网络技术有限公司 版权所有 http://www.uweixin.com/
*│  警告:本计算机程序受著作权法和国际公约的保护,
*│  未经授权擅自复制传播本程序的部分或全部,可能受到严厉的民事及刑事制裁,并在法律的许可范围内受到可能的起诉。
*└──────────────────────────────────────────────────────────────┘
*┌──────────────────────────────────────────────────────────────┐
*│ 命名空间: MuXue.WeTao.Mall.Core.kuaidi100                                  
*│ 类    名: KuaiDi100Helper                                      
*└──────────────────────────────────────────────────────────────┘
*/
using Microsoft.Extensions.Logging;
using MuXue.WeTao.Mall.Core.Http;
using MuXue.WeTao.Mall.Core.kuaidi100.Common;
using MuXue.WeTao.Mall.Core.kuaidi100.Model;
using MuXue.WeTao.Mall.Core.kuaidi100.Utils;
using MuXue.WeTao.Mall.Core.System;
using MuXue.WeTao.Mall.Domain.Model;
using MuXue.WeTao.Mall.Domain.Model.Common;
using MuXue.WeTao.Mall.Domain.Model.Request;
using MuXue.WeTao.Mall.IRepository;
using MuXue.WeTao.Mall.Repository;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;

namespace MuXue.WeTao.Mall.Core.kuaidi100
{
    public  class KuaiDi100Helper
    {
        private ILogger _logger;
        private MuXueConfigHelper _configHelper;
        HttpClient _client;

        /// <summary>
        /// 沐雪-多租宝-快递100物流信息查询帮助类
        /// </summary>
        /// <param name="logger"></param>
        /// <param name="configHelper"></param>
        public KuaiDi100Helper(ILogger<KuaiDi100Helper> logger, HttpClient client, MuXueConfigHelper configHelper)
        {
            _configHelper = configHelper;
            _logger = logger;
            _client = client;
        }

        /// <summary>
        /// 实时快递查询接口
        /// </summary>
        /// <param name="tenant_id"></param>
        /// <param name="shop_code"></param>
        /// <param name="com">查询的快递公司的编码, 一律用小写字母</param>
        /// <param name="num">查询的快递单号, 单号的最大长度是32个字符</param>
        /// <param name="phone">收、寄件人的电话号码(手机和固定电话均可,只能填写一个,顺丰单号必填,其他快递公司选填。如座机号码有分机号,分机号无需上传。)</param>
        /// <returns></returns>
        public async Task<QueryTackResult> QueryTrack(string shop_code,string com,string num,string phone="13816330315")
        {
            QueryTackResult result = new QueryTackResult();
            try
            {

                TenantConfig config = await _configHelper.GetTenantAllAsync(shop_code);

                QueryTrackParam queryTrackParam = new QueryTrackParam();
                if (com== "shunfeng")
                {
                      queryTrackParam = new QueryTrackParam()
                    {
                        com = com,
                        num = num,
                        phone = phone
                    };
                }
                else
                {
                      queryTrackParam = new QueryTrackParam()
                    {
                        com = com,
                        num = num, 
                    };
                }
               

                QueryTrackReq query = new QueryTrackReq()
                {
                    customer = config.KuaiDi100CustomerID,
                    sign = SignUtils.GetMD5(queryTrackParam.ToString() + config.KuaiDi100Key + config.KuaiDi100CustomerID),
                    param = queryTrackParam
                };
                var requestParam = ObjectToDictionaryUtils.ObjectToMap(query);
                if (requestParam == null)
                {
                    return null;
                }
                 
                 result = await HttpClientHelper.PostFormAsync<QueryTackResult>(_client, ApiInfoConstant.QUERY_URL, requestParam);
                 
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"快递100实时快递查询接口异常:{ex.Message}");
                return null;
            }
            return result;
        }


    }
}
View Code

 

 

 

 

 

购买请联系官方qq: 23002807

手机(微信):13816330315