草庐IT

基于SpringCloud + Oauth2.0 + ShiroRedis + JWT + Gateway + Nacos + Nginx + Vue实现的SaaS数字商城系统

KJ.JK 2023-08-04 原文

文章目录


🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈
 
🍂个人博客首页: KJ.JK
 
欢迎大家点赞👍收藏💖评论💬关注🔒
 
💖源码获取 | 💻学习交流 | 🤝商务合作 | 💨私信作者


一、什么是SaaS?

SaaS的英文全称是Software as a Service,意思是软件即服务,是云计算的其中一种服务模式
SaaS是一种通过Internet提供集中托管应用程序的方式,企业用户一般通过客户端或网页来使用,无需购买、安装或维护任何软件及硬件,因此SaaS应用程序又被称为"基于Web的软件" 或 "托管软件"


二、数字商城系统介绍

该系统意在通过技术封装,让企业无需代码开发,帮助企业一键生成小程序、公众号,让企业拥有独立品牌的自营商城数字商城支持自定义装修,面向的是全国用户,订单走快递物流可以快速帮助线上电商用户搭建自己的小程序商城,产品类似有赞的微商城系统


三、技术栈以及项目特点


⭐新版saas技术栈使用⭐

  • SpringCloud + Oauth2.0 + ShiroRedis + JWT + Gateway + Nacos + Nginx + Vue
     

⭐项目特点⭐

  • 项目采用Spring+SpringMVC+Mybatis主流开源框架,遵循MVC架构,设计轻巧,使用简单,开发人员接手与二次开发简单易懂;
  • 项目完成了对阿里云、腾讯云、微信生态的快速接入与代码示例,并成功运用到了商业中,方便大家学习对OSS/COS/短信服务/快递服务/公众号/小程序/微信开放平台/微信支付的技术点与应用场景的学习
  • 小程序与公众号第三方授权集成,让开发者与企业省去繁琐的配置,对AppId/AppSecret集成彻底告别
  • 微信支付第三方授权集成,让开发者与企业省去繁琐的配置,让企业服务成本大大降低;
  • 小程序代码自动push与升级,公众号基本常规功能(自定义菜单、图文、素材,群发)全部接管,让企业无需登录微信官方入口,统一化管理企业信息与数据
  • 微信消息通知与模板自动同步与管理,让企业场景融合无障碍;
  • 自定义装修功能,实现企业电商场景多元化,解决不同行业不同模板,让企业需求得到真正解决;
  • 丰富的营销工具,解决企业微营销需求,并集中化管理。拼团活动、积分活动、砍价,优惠券,卡券,礼品卡,邀请有礼等营销活动,方便二次开发;
  • 不同企业数据隔离,真正实现SAAS数据隔离模型,让企业数据互不干扰
  • 不同企业不同域名自动分配,让企业小程序、公众号前端隔离,避免因为公众号或者小程序推广时导致域名被检测导致的封号问题出现,即不同企业不同域,意在防止saas平台中的企业受到其他企业的影响

四、系统设计


数字商城主要功能模块包括 首页、会员管理、商品管理、订单管理、营销管理、积分管理、店铺管理、基础设置、系统管理


          (1)首页模块: 该模块包含数字商城的首页


          (2)会员管理模块: 该模块包含微页面,全店风格,底部菜单,底部导航


          (3)商品管理模块: 该模块包含品牌管理、商品分类、商品列表、商品分组、运营分类、商品标签、商品库存、商品回收站


          (4)订单管理模块: 该模块包含全部订单、待付款订单、待发货订单、已发货订单、已完成订单、已退款订单、订单评价


          (5)营销管理模块: 该模块包含礼包分类、礼包主题、电子卡券、卡卷实例、卡券中心、邀请有礼、储值有礼、充值订单、拼团管理、拼团订单


          (6)积分管理模块: 该模块包含综合统计、店铺的概况、订单的概况、客户的分析


          (7)店铺管理模块: 该模块包含店铺装修、风格样式、底部导航、首页广告


          (8)基础设置模块: 该模块包含第三方应用设置,微信小程序、微信支付、企业微信、消息模板、协议设置


          (9)系统设置模块: 该模块包含一些常用的系统设置


五、系统部分功能演示


🚩店铺管理功能演示图





🚩商品管理功能演示图



🚩订单功能演示图



🚩营销管理功能演示图




六、系统部分源代码


/**
 * 营销管理-》优惠券
 * @company http://www.wei-it.com 微邦互联
 */
@Controller
@RequestMapping(value = "/ump/coupons")
public class CouponsController extends AdminController {

	public static Logger logger= Logger.getLogger(CouponsController.class);

	@Resource
	private CouponService couponService;

	@Resource
	ParameterService parameterService;

	@Resource
	WeixinOpenService weixinOpenService;
	  
	/**
	 * 优惠券》优惠券列表
	 */
	@RequestMapping("/couponsList")
	public UIview couponsList(){
		logger.info("进入CouponsController-couponsList,优惠券列表展示");
		
		UIview view = UIView("/center/ump/coupons/couponsList",false);
    	FormMap formMap=getFormMap();
    	PageHelper.startPage(formMap.getPage(), formMap.getRows());
    	List<E> list=couponService.selectList(formMap);
       
    	view.addObject("pageInfo", new PageInfo<E>(list));
    	view.addObject("queryParam", formMap);
		return view;
	}
	
    /**
     * 优惠券》优惠券创建
     */
	@RequestMapping("/create")
	public UIview create(){
		UIview view=UIView("/center/ump/coupons/couponsCreate", false);
		return view;
	}
	
    /**
     * 优惠券 》编辑
     */
	@RequestMapping("/edit")
	public UIview edit(){
		logger.info("进入CouponsController-edit,优惠券编辑");
		
		UIview view=UIView("/center/ump/coupons/couponsCreate", false);
		FormMap formMap=getFormMap();
		E coupon=couponService.selectCoupon(formMap);
		
		view.addObject("coupon", coupon);
		return view;
	}
	
    /**
     * 优惠券 》优惠券新增保存
     * @throws Exception 
     */
	@RequestMapping("/save")
	public ModelAndView save(){
		logger.info("进入CouponsController-save,优惠券新增保存");
		
		UIview view=UIView("couponsList", true);
	    FormMap formMap=getFormMap();
	    //判断优惠形式type=1,2,3
	    if(formMap.getInt("type")==1){//优惠形式表示“指定金额”
	        formMap.set("coupon_discount", null);
	        formMap.set("random_min_price", null);
	        formMap.set("random_max_price", null);
	    }else if(formMap.getInt("type")==2){//优惠形式表示“打折”
	        formMap.set("coupon_price", null);
	        formMap.set("random_min_price", null);
	        formMap.set("random_max_price", null);
	    }else{//优惠形式表示“指定金额(随机)”
	        formMap.set("coupon_discount", null);
	        formMap.set("coupon_price", null);
	    }
	    //判断生效条件is_condition=-1,1
		//生效条件表示“无条件”
	    if(formMap.getInt("is_condition")==-1){
	        formMap.set("condition_price", null);
	    }
		formMap.put("state",1);

	    //判断使用有效期方式use_type=1 固定范围  ,2  指定时间范围
		//使用有效期表示“固定范围”
	    if(formMap.getInt("use_type")==1){
	        formMap.set("from_day", null);
	        formMap.set("to_day", null);
	        //如果开始时间小于当前时间   则优惠券处于未生效状态  ,其他情况都是生效状态
			if (System.currentTimeMillis()< DateUtil.getTimeByString(formMap.getStr("start_time"),"yyyy/MM/dd HH:mm:ss")){
				formMap.put("state",-2);
			}
	    }else{
	        formMap.set("start_time", null);
	        formMap.set("end_time", null);
	    }
	    //判断没人领取限制
		//使用有效期表示“不限制领取熟量”
	    if(formMap.getInt("maxLimitType")==1){
	        formMap.set("max_limit", 0);
	    }
	    //判断可使用商品
		//使用有效期表示“全网商品通用”
	    if(formMap.getInt("product_range_type")==1){
	        formMap.set("product_ids", null);
	    }else{
	        formMap.set("product_ids", formMap.getStr("selectProductValues"));
	    }
	    //判断是新增还是修改
		//新增优惠券判断
		if(StringUtils.isEmpty(formMap.getStr("validate_id"))){
			couponService.insert(formMap);
		}else{//修改优惠券
			couponService.edit(formMap);
		}
		return view;
	}
	
	/**
	 * 优惠券》优惠券列表》生效&不生效
	 */
	@RequestMapping("/state")
	public UIview state(){
		logger.info("进入CouponsController-state,优惠券生效&不生效操作");
		
		UIview view=UIView("couponsList", true);
		FormMap formMap=getFormMap();
		formMap.set("onlyUpdateState", "state");
		couponService.edit(formMap);
		return view;
	}
	
	/**
	 * 优惠券》优惠券列表》删除
	 */
	@RequestMapping("/remove")
	public UIview remove(){
		logger.info("进入CouponsController-remove,优惠券删除");
		
		UIview view=UIView("couponsList", true);
		FormMap formMap=getFormMap();
		formMap.set("state", "0");
		List<E> list=couponService.selectCouponUserList(formMap);
		if(list.size() > 0){//该优惠券还有未使用的用户
			view.addErrorMessage("删除失败,该优惠券还有未使用的用户!");
		}else{
			couponService.remove(formMap);
			view.addPNotifyMessage("优惠券删除成功!");
		} 
		return view;
	}
 
    /**
     * 优惠券 派发用户列表 
     */ 
	@RequestMapping("/couponsGive")
	public UIview couponsGive(){
		logger.info("进入CouponsController-couponsGive,优惠券推广与派发");
		
		UIview view = UIView("/center/ump/coupons/couponsGive",false);
    	FormMap formMap=getFormMap();
    	PageHelper.startPage(formMap.getPage(), formMap.getRows());
    	//查询没有领取该优惠券的用户列表与领取了但是没有超过优惠券最大设置限购数的用户列表

    	List<E> list=couponService.selectNoCouponUserList(formMap);
    	
    	view.addObject("pageInfo", new PageInfo<E>(list));
    	view.addObject("queryParam", formMap);


		E publicInfo=(E)this.getSession().getAttribute("publicInfo");
		if (publicInfo!=null){
			//获取公众号优惠券地址  和 链接
			formMap.put("item_name","COUPONSMPQRCODE");
			E paramInfo = parameterService.selectShopParamByName(formMap);
			this.getSession().setAttribute("couponMpQrCodeUrl",String.format(Constants.COUPON_QR_CODE,publicInfo.getStr("authorizer_app_id")));
			if (paramInfo!=null){
				this.getSession().setAttribute("couponMpQrcode",paramInfo.getStr("item_value"));
			}else {
				WeiitQrCodeUtil qrCodeUtil = new WeiitQrCodeUtil();
				String qrCodePath = qrCodeUtil.createQRCodeAndUploadQcloud(String.format(Constants.COUPON_QR_CODE,publicInfo.getStr("authorizer_app_id")));
				//入库
				FormMap insertParam = new FormMap();
				insertParam.put("item_code","COUPONQRCODE");
				insertParam.put("item_name","COUPONSMPQRCODE");
				insertParam.put("item_value",WeiitUtil.getFileDomain()+qrCodePath);
				insertParam.put("item_desc","公众号优惠券领取二维码");
				insertParam.put("state",0);
				insertParam.put("shop_id",formMap.get("shop_id"));
				parameterService.insert(insertParam);
				this.getSession().setAttribute("couponMpQrcode",WeiitUtil.getFileDomain()+qrCodePath);
			}
		}
		E miniPublicInfo = (E) this.getSession().getAttribute("miniPublicInfo");
		if (miniPublicInfo!=null){
			//获取公众号优惠券地址  和 链接
			formMap.put("item_name","COUPONSMAQRCODE");
			E paramInfo = parameterService.selectShopParamByName(formMap);
			if (paramInfo!=null){
				this.getSession().setAttribute("couponMaQrcode",paramInfo.getStr("item_value"));
			}else {
				try {
					formMap.put("appid",miniPublicInfo.getStr("authorizer_app_id"));
					File file = weixinOpenService.getInstance(formMap).getWxOpenComponentService().getWxMaServiceByAppid(miniPublicInfo.getStr("authorizer_app_id")).getQrcodeService().createWxaCode("pages/Receive_coupons/Receive_coupons",225);
					String qrCodePath = WeiitUtil.uploadFile(FileUtils.readFileToByteArray(file),"png");

					FormMap insertParam = new FormMap();
					insertParam.put("item_code","COUPONQRCODE");
					insertParam.put("item_name","COUPONSMAQRCODE");
					insertParam.put("item_value",WeiitUtil.getFileDomain()+qrCodePath);
					insertParam.put("item_desc","小程序优惠券领取二维码");
					insertParam.put("state",0);
					insertParam.put("shop_id",formMap.get("shop_id"));
					parameterService.insert(insertParam);
					this.getSession().setAttribute("couponMaQrcode",WeiitUtil.getFileDomain()+qrCodePath);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}

		return view;
	}
	
	/**
	 * 派发优惠券  保存 
	 * @return
	 * @throws Exception
	 */
	@RequestMapping("/couponsGiveSave")
	public UIview couponsGiveSave(){
		logger.info("进入CouponsController-couponsGiveSave,优惠券推广提交");
		
		FormMap formMap=getFormMap();
		String message=couponService.insertCouponUserTrans(formMap);
		UIview view = UIView("couponsGive?validate_id="+formMap.getStr("validate_id")+"&validate_id_token="+formMap.getStr("validate_id_token"),true);
		if(message==null){
			view.addPNotifyMessage("会员优惠券派送成功!");
		}else{
			view.addErrorMessage(message);
		}
		return view;
	}
	
	/**
	 * 派发优惠券记录
	 * @return
	 * @throws Exception
	 */
	@RequestMapping("/couponsUserList")
	public UIview couponsUserList(){
		logger.info("进入CouponsController-couponsUserList,发优惠券记录");
		
		UIview view = UIView("/center/ump/coupons/couponsUserList",false);
    	FormMap formMap=getFormMap();
    	E coupon = couponService.selectOne(formMap);
    	PageHelper.startPage(formMap.getPage(), formMap.getRows());
    	//查询该优惠券已经派发过的用户
    	List<E> list=couponService.selectCouponUserList(formMap);
    	
    	view.addObject("pageInfo", new PageInfo<E>(list));
    	view.addObject("queryParam", formMap);
    	view.addObject("coupon", coupon);
		return view; 
	}

	
	/**
	 * 派发优惠券》回收
	 * @return
	 * @throws Exception
	 */
	@RequestMapping("/couponsUserRemove")
	public UIview couponsUserRemove(){
		logger.info("进入CouponsController-couponsUserRemove,派发优惠券 回收");
		
		FormMap formMap=getFormMap();
		String message=couponService.removeCouponUserTrans(formMap);
		UIview view = UIView("couponsUserList?validate_id="+formMap.getStr("validate_id")+"&validate_id_token="+formMap.getStr("validate_id_token"),true);
		if(message==null){
			view.addPNotifyMessage("优惠券回收成功!");
		}else{
			view.addErrorMessage(message);
		}
		return view;
	}


	/**
	 * 装修页面 获取优惠券列表
	 *
	 *
	 * */
	@RequestMapping(value = "/getCouponList",method = RequestMethod.GET)
	@ResponseBody
	public E getCouponList(@RequestParam String token,String coupon_name) {
		logger.info("进入CouponsController-getCouponList,获取优惠券列表");
		FormMap formMap=new FormMap();
		E result = new E();
		try {
			formMap.put("shop_id", DesUtil.decrypt(token));
		} catch (Exception e) {
			e.printStackTrace();
			logger.error("token 解密失败");
			result.put("couponList",null);
			return result;
		}
		formMap.put("coupon_name",coupon_name);
		List<E> list = couponService.selectList(formMap);
		result.put("couponList",list);
		//绑定上一次参数
		return result;
	}


	/**
	 * 微页面  优惠券展示   不展示是否领取  过滤失效优惠券
	 * @author lhq
	 * @date 2018年5月10日
	 * */

	@RequestMapping(value = "/couponListByIds",method = RequestMethod.GET)
	@ResponseBody
	public String couponListByIds(@RequestParam String token,String coupon_ids,Integer couponType){
		logger.info("ActivityController-couponListByIds,微页面  优惠券展示");
		FormMap formMap = new FormMap();
		try {
			formMap.put("shop_id", DesUtil.decrypt(token));
		} catch (Exception e) {
			e.printStackTrace();
			logger.error("token 解密失败");
			return "";
		}
		formMap.put("couponType",couponType);
		if (!StringUtils.isEmpty(coupon_ids) && couponType==0){
			formMap.put("coupon_ids", StringUtils.strip(coupon_ids, "[]").split(","));
		}
		formMap.put("end_time",new Date());
		List<E> list = couponService.couponListByIds(formMap);
		return toJsonAPI(list);
	}
	
}

🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈
 
🍂个人博客首页: KJ.JK
 
欢迎大家点赞👍收藏💖评论💬关注🔒
 
💖源码获取 | 💻学习交流 | 🤝商务合作 | 💨私信作者


作者:KJ.JK

文章对你有所帮助的话,欢迎给个赞或者 star,你的支持是对作者最大的鼓励,不足之处可以在评论区多多指正,交流学习

有关基于SpringCloud + Oauth2.0 + ShiroRedis + JWT + Gateway + Nacos + Nginx + Vue实现的SaaS数字商城系统的更多相关文章

  1. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  2. 计算机毕业设计ssm+vue基本微信小程序的小学生兴趣延时班预约小程序 - 2

    项目介绍随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱小学生兴趣延时班预约小程序的设计与开发被用户普遍使用,为方便用户能够可以随时进行小学生兴趣延时班预约小程序的设计与开发的数据信息管理,特开发了小程序的设计与开发的管理系统。小学生兴趣延时班预约小程序的设计与开发的开发利用现有的成熟技术参考,以源代码为模板,分析功能调整与小学生兴趣延时班预约小程序的设计与开发的实际需求相结合,讨论了小学生兴趣延时班预约小程序的设计与开发的使用。开发环境开发说明:前端使用微信微信小程序开发工具:后端使用ssm:VU

  3. ruby-on-rails - 如何用不同的用户运行nginx主进程 - 2

    A/ctohttp://wiki.nginx.org/CoreModule#usermaster进程曾经以root用户运行,是否可以以不同的用户运行nginxmaster进程? 最佳答案 只需以非root身份运行init脚本(即/etc/init.d/nginxstart),就可以用不同的用户运行nginxmaster进程。如果这真的是你想要做的,你将需要确保日志和pid目录(通常是/var/log/nginx&/var/run/nginx.pid)对该用户是可写的,并且您所有的listen调用都是针对大于1024的端口(因为绑定(

  4. ruby-on-rails - Rails 两条腿的 OAuth 提供商? - 2

    我有一个Rails2.3.5应用程序,其中包含我希望保护的API。没有用户-它是一个应用到应用风格的网络服务(更像是亚马逊服务而不是facebook),所以我想使用两条腿的OAuth方法来实现它。我一直在尝试使用oauth-plugin服务器实现作为开始:http://github.com/pelle/oauth-plugin...但它的构建需要三足(网络重定向流)oauth。在我深入研究对其进行更改以支持两条腿之前,我想看看是否有更简单的方法,或者是否有人有更好的方法让Rails应用程序实现成为两条腿的OAuth提供程序。 最佳答案

  5. (附源码)vue3.0+.NET6实现聊天室(实时聊天SignalR) - 2

    参考文章搭建文章gitte源码在线体验可以注册两个号来测试演示图:一.整体介绍  介绍SignalR一种通讯模型Hub(中心模型,或者叫集线器模型),调用这个模型写好的方法,去发送消息。  内容有:    ①:Hub模型的方法介绍    ②:服务器端代码介绍    ③:前端vue3安装并调用后端方法    ④:聊天室样例整体流程:1、进入网站->调用连接SignalR的方法2、与好友发送消息->调用SignalR的自定义方法 前端通过,signalR内置方法.invoke()  去请求接口3、监听接受方法(渲染消息)通过new signalR.HubConnectionBuilder().on

  6. 【云原生】SpringCloud-Spring Boot Starter使用测试 - 2

    目录SpringBootStarter是什么?以前传统的做法使用SpringBootStarter之后starter的理念:starter的实现: 创建SpringBootStarter步骤在idea新建一个starter项目、直接执行下一步即可生成项目。 在xml中加入如下配置文件:创建proterties类来保存配置信息创建业务类:创建AutoConfiguration测试如下:SpringBootStarter是什么? SpringBootStarter是在SpringBoot组件中被提出来的一种概念、简化了很多烦琐的配置、通过引入各种SpringBootStarter包可以快速搭建出一

  7. ruby - 如何使用 omniauth/oauth 对每秒登录数进行基准测试? ( ruby +rspec) - 2

    我想用一个(自己的)omniauth提供商来衡量每秒可以登录多少次。我需要了解此omniauth/oauth请求的性能如何,以及此身份验证是否具有可扩展性?到目前为止我得到了什么:defperformance_auth(user_count=10)bm=Benchmark.realtimedouser_count.timesdo|n|forkdoclick_on'Logout'omniauth_config_mock(:provider=>"foo",:uid=>n,:email=>"foo#{n}@example.net")visit"/account/auth/foo/"enden

  8. ruby-on-rails - 如何编写 Rails 4 测试以使用 omniauth-google-oauth2 gem 创建 session ? - 2

    我正在尝试为使用omniauth-google-oauth2gem创建session编写测试。我是否需要将env["omniauth.auth"]变量与post:create一起传递?也许当我试图这样做时,我做错了。我得到的错误如下所示...Rake测试错误1)Error:SessionsControllerTest#test_should_get_create:NoMethodError:undefinedmethod`provider'fornil:NilClassapp/models/user.rb:6:in`from_omniauth'app/controllers/sessi

  9. ruby-on-rails - 将 OAuth 与 ActiveResource 一起使用的最简单方法是什么? - 2

    我正在使用一些旧代码并使用ActiveResource进行非常基本的Twitter集成。我想尽可能少地接触应用程序代码,并在仍然使用ActiveResource的同时引入OAuth。不幸的是,我找不到简单的方法来做到这一点。我确实遇到了oauth-active-resourcegem,但它并没有完全记录下来,而且它似乎是为创建完整的API包装器库而设计的。您可以想象,我想避免为这一遗留更改创建整个TwitterActiveResourceAPI包装器。有什么成功案例吗?在我的例子中,离开ActiveResource可能比让它工作更快。我很高兴被证明是错误的!

  10. ruby-on-rails - rails 中的 Omniauth-twitter:OAuth::Unauthorized 401 - 2

    我在使用Twitter进行基本的omniauth身份验证时被封锁了2天。我在简单的omniauth上跟随RyanBates的railscast,但无法通过OAuth::Unauthorized401异常,当我尝试登录时引发。请帮忙!我的代码粘贴在下面:twitterinfo:website:[http://127.0.0.1:3000]callbarckurl:[http://127.0.0.1:3000/auth/twitter/callback]//路线.rbSentimentalist::Application.routes.drawdoresources:dashboard,o

随机推荐