zenglMall是使用zengl语言开发商城系统的项目,zenglMall需要通过zenglServer来执行zengl脚本。首先配置zenglServer,让其网站根目录指向zenglMall

    页面导航:

项目下载地址:

    zenglMall源代码的相关地址:https://github.com/zenglong/zenglMall  当前版本对应的tag标签为:v0.1.0

zenglMall v0.1.0:

    zenglMall是使用zengl语言开发商城系统的项目,zenglMall需要通过zenglServer来执行zengl脚本。首先配置zenglServer,让其网站根目录指向zenglMall:

................................. (zenglServer的config.zl配置,省略N行)
webroot = "/home/zengl/zenglMall"; // web根目录,指向zenglMall所在的目录

session_dir = "my_sessions"; // 会话目录
session_expire = 1440; // 会话默认超时时间(以秒为单位)
session_cleaner_interval = 3600; // 会话文件清理进程的清理时间间隔(以秒为单位)

    然后运行zenglServer(v0.1.0的zenglMall对zenglServer的最低版本要求是v0.23.0,还需要开启mysql,magick,pcre以及openssl模块):

[zengl@localhost zenglServer]$ ./zenglServer -v
zenglServer version: v0.23.0
zengl language version: v1.8.3
[zengl@localhost zenglServer]$ ./zenglServer 
[zengl@localhost zenglServer]$ tail -f logfile
port: 8083 process_num: 1
webroot: /home/zengl/zenglMall
session_dir: my_sessions session_expire: 1440 cleaner_interval: 3600
remote_debug_enable: False remote_debugger_ip: 127.0.0.1 remote_debugger_port: 9999 zengl_cache_enable: False shm_enable: False shm_min_size: 307200
verbose: True request_body_max_size: 204800, request_header_max_size: 5120 request_url_max_size: 1024
URL_PATH_SIZE: 120 FULL_PATH_SIZE: 200
pidfile: zenglServer.pid
bind done
accept sem initialized.
process_max_open_fd_num: 1024 
Master: Spawning child(0) [pid 2375] 
Master: Spawning cleaner [pid 2376] 
------------ cleaner sleep begin: 1607756884
epoll max fd count : 896

创建数据库表结构:

    要访问商城首页和进入后台管理界面,还需要创建数据库表结构,首先修改zenglMall根目录中的config.zl配置:

config['db_host'] = 'localhost';   // 填写mysql数据库ip
config['db_port'] = 3306;          // 填写mysql数据库端口
config['db_user'] = 'root';        // 填写mysql用户名
config['db_passwd'] = '123456';    // 填写mysql密码
config['db_name'] = 'testmall';    // 填写mysql数据库名
config['version'] = '0.1.0';       // zenglMall版本号,无需修改
config['site_name'] = 'zenglMall'; // 站点名称
config['site_desc'] = 'mall made by zengl language'; // 站点描述
// 支付宝的APPID(发起请求的应用ID)
config['app_id'] = '';
// 支付完成后的异步通知地址,必须是外网可以访问到的地址
config['notify_url'] = 'http://domain_url/notify_url.zl';
// 支付完成后跳转返回到商家的地址
config['return_url'] = 'http://domain_url/return_url.zl';
// 签名方式,目前测试脚本只支持最新的RSA2方式,所以不需要修改
config['sign_type'] = 'RSA2';
// 支付宝网关,沙箱的网关地址和正式环境的网关地址不同
config['gateway_url'] = 'https://openapi.alipaydev.com/gateway.do';
// 商户私钥
config['merchant_private_key'] = '';
// 支付宝公钥
config['alipay_public_key'] = '';

    请确保上面数据库配置的正确性,如果没有创建过testmall(假如你在配置中使用了testmall作为数据库名的话),就先创建该数据库。如果要测试支付功能的话,则还需要配置与支付宝相关的APPID,商户私钥等。

    假设zenglServer所在的系统的ip为192.168.1.113,那么访问 http://192.168.1.113:8083/install/create_table.zl 该脚本会自动在数据库中创建所需的表结构,例如 admin_users(后台管理用户表),category(商品分类表),goods(商品表)等,并在admin_users表中插入一条初始数据。该脚本执行成功后,会返回后台用户名,密码之类的信息,如下所示:

创建zenglMall数据库表结构

    安装会生成install.lock锁文件,以防止误操作。在有锁文件的情况下,再次执行create_table.zl脚本,就会提示 lock file exists ,并阻止脚本继续执行。

商城的前台及后台介绍:

    安装完毕后,就可以通过 http://192.168.1.113:8083/index.zl 的地址看到商城的首页了:

初始化安装后的商城首页

    初始化安装后,一开始首页的导航栏只有一个会员中心的菜单,更多的菜单需要在后台通过添加顶层分类的形式来添加。页面上面还没有商品信息,商品信息也需要在管理后台添加。

    接下来,我们就可以使用 admin 和 admin@123456 的初始后台管理员的用户名和密码,通过 http://192.168.1.113:8083/admin/login.zl 登录后台。

    登录成功后的后台地址:http://192.168.1.113:8083/admin/admin.zl ,此地址是概览页面,该页面可以看到一些基本信息,例如:mysql版本号,zenglServer版本号,zengl语言版本等等,还可以在管理后台添加分类,添加商品,以及进行订单管理等:

后台概览页面

    在后台添加了分类和商品后,就可以在商城首页看到这些分类和商品信息了:

添加分类和商品信息后的首页示例

    点击商品链接可以进入商品详情页面:

商品详情页面

    如果要测试支付功能,除了要配置上面提到过的支付宝的APPID,商户私钥等配置外,还必须确保配置中的异步通知地址能被外网访问到(因为支付完成后,支付宝会将支付完成情况通过这个地址反馈给服务器,因此,ip地址或域名必须能被外网访问到),对于本地测试环境,可以利用ngrok之类的工具,将外网请求转发到本地测试环境。在配置好这些后,就可以点击商品详情页面中的立即购买按钮,进入确认支付页面(如果没有登录过,会提示登录,如果没注册过,可以在登录页面点击立即注册按钮进行注册):

确认支付页面

     在确认支付页面,需要填写收货人的姓名,电话,地址等信息,填写完后,点击确认支付按钮,就会跳转到支付宝的支付页面:

支付宝的支付页面

    在支付页面完成支付操作后,就会跳转回商家页面,并提示支付成功的信息:

支付完成后返回的商家页面

    在前台用户会员中心的订单列表页面,以及管理后台的订单列表页面都可以看到相关的订单信息。

通过会员中心的订单列表,可以查看某个订单的订单详情

    在管理后台的订单详情页面可以将订单设置为待收货状态(表示已发货),还可以在订单详情页面设置商家备注,将快递单号等信息写入进去,这样前台用户就可以知道快递信息了。

    而前台用户在会员中心的订单详情页面可以执行确认收货的操作。前台用户还可以在会员中心修改用户信息,例如修改默认的收货地址等,还可以在会员中心修改密码。

商城的源码介绍:

    商城前台页面(例如:首页,点击分类后进入的商品列表页及商品详情页面等)相关的脚本都位于根目录中,包括config.zl商城的配置脚本也在根目录。

    和前台用户会员中心相关的脚本,放置在user_admin目录中,除了前台用户的登录脚本,登录脚本(login.zl)还是在根目录。

    商城后台页面相关的脚本,则放置在admin目录中。大部分脚本的代码中都添加了相关的注释信息,方便对代码进行分析,例如根目录中的mysql.zl脚本:

// Mysql数据库类,提供一些和Mysql数据库操作相关的方法
class Mysql
	con;
	error_tpl;
	/** 
	 * 当操作数据库发生错误时,直接退出脚本,同时会返回500错误给客户端,所有的500错误对应的具体错误原因都会记录到zenglServer日志中,
	 * 如果使用了bltFatalErrorCallback模块函数设置过运行时错误回调函数的话,还会调用用户自定义的回调函数来处理错误信息
	 */
	fun error(obj, sql='')
		Mysql obj;
		data['error'] = mysqlError(obj.con);
		mysqlClose(obj.con);
		if(obj.error_tpl)
			data['title'] = 'Mysql错误';
			print bltMustacheFileRender(obj.error_tpl,data);
		endif
		bltExit(data['error'] + (sql ? ' sql: ' + sql : ''));
	endfun

	// 初始化mysql数据库连接,并将mysql连接设置到obj的con成员中
	fun init(obj, config, error_tpl)
		Mysql obj;
		obj.error_tpl = error_tpl;
		obj.con = mysqlInit();
		if(!mysqlRealConnect(obj.con, config['db_host'], config['db_user'], 
					config['db_passwd'], config['db_name'], config['db_port']))
			Mysql.error(obj);
		endif
		if(mysqlSetCharacterSet(obj.con, "utf8"))
			Mysql.error(obj);
		endif
	endfun

	// 根据sql语句执行查询操作,返回一条查询记录(返回数组的各成员对应该条查询记录的各个字段)
	fun fetchOne(obj, sql)
		Mysql obj;
		if(mysqlQuery(obj.con, sql))
			Mysql.error(obj);
		endif
		result = mysqlStoreResult(obj.con);
		if(!mysqlFetchResultRow(result, &result_array))
			result_array = bltArray();
		endif
		mysqlFreeResult(result);
		return result_array;
	endfun

	// 根据sql语句执行查询操作,返回所有的查询记录(返回数组的每个成员都对应一条查询记录)
	fun fetchAll(obj, sql)
		Mysql obj;
		if(mysqlQuery(obj.con, sql))
			Mysql.error(obj, sql);
		endif
		result = mysqlStoreResult(obj.con);
		return_array = bltArray();
		while(mysqlFetchResultRow(result, &result_array))
			return_array[] = result_array;
		endwhile
		mysqlFreeResult(result);
		return return_array;
	endfun

	// 将str对应的语句进行mysql转义,让其可以安全的用于sql语句中(通常用于拼接sql语句时,需要执行转义操作)
	fun Escape(obj, str)
		Mysql obj;
		return mysqlRealEscapeString(obj.con, bltStr(str));
	endfun

	// 将datas数组作为数据库记录(数组中的每个成员都对应记录中的一个字段,成员key对应字段的列名,成员的值对应字段的值)插入到table表中
	fun Insert(obj, table, datas)
		Mysql obj;
		vals = keys = '';
		len = bltCount(datas);
		for(i=0,j=0;bltIterArray(datas,&i,&k,&v);)
			keys += "`" + Mysql.Escape(obj, k) + "`";
			vals += "'" + Mysql.Escape(obj, v) + "'";
			if(++j < len)
				keys += ',';
				vals += ',';
			endif
		endfor
		if(len > 0)
			sql = "INSERT INTO `"+table+"` (" + keys + ") VALUES (" + vals + ")";
			if(mysqlQuery(obj.con, sql))
				Mysql.error(obj, sql);
			endif
		endif
	endfun

	// 将datas数组作为数据库记录(数组中的每个成员都对应记录中的一个字段,成员key对应字段的列名,成员的值对应字段的值)更新到table表中,
	// where表示更新条件,即通过sql的where语句来指定哪些数据库记录需要被更新
	fun Update(obj, table, datas, where = '')
		Mysql obj;
		key_vals = '';
		len = bltCount(datas);
		for(i=0,j=0;bltIterArray(datas,&i,&k,&v);)
			key_vals += "`" + Mysql.Escape(obj, k) + "`=" + "'" + Mysql.Escape(obj, v) + "'";
			if(++j < len) key_vals += ','; endif
		endfor
		if(len > 0)
			sql = "UPDATE `"+table+"` SET " + key_vals + (where != '' ? ' WHERE ' + where : '');
			if(mysqlQuery(obj.con, sql))
				Mysql.error(obj, sql);
			endif
		endif
	endfun

	// 执行原始的sql语句,脚本中也可以手动拼接原始的sql语句来执行相关的数据库操作(例如插入,更新之类的语句)
	fun Exec(obj, sql)
		Mysql obj;
		if(mysqlQuery(obj.con, sql))
			Mysql.error(obj);
		endif
	endfun
endclass

    zenglMall的前端模板是使用bootstrap + vue来编写的,因此,请尽量使用火狐,谷歌或者微软的Edge浏览器来进行测试,IE11在测试当前版本时发现,在后台编辑商品信息时会自动过滤掉CKEditor编辑器中的图片信息,所以IE11之类的IE系列的浏览器还存在兼容问题,在后续版本开发中,如果有时间的话有可能会去处理这些浏览器兼容问题,不过就当前版本而言,还是尽量使用火狐,谷歌之类的浏览器来进行测试。

    zenglMall使用MIT开源协议,没有什么使用上的限制,你可以对它进行任意的修改,发布,以及上线进行商业运作等。在根目录的readme.md文档中也记录了zenglMall的安装和使用说明,如果要使用nginx进行反向代理的话(由nginx处理静态文件请求,由zenglServer来运行zengl脚本),也可以在readme.md文档中找到相关的说明。

结束语:

    人生只有一次,如果不按照自己的意愿去活,那才是最大的失败。

—— 创业时代

 

上下篇

下一篇: zenglMall v0.2.0 增加商品库存,采用付款后减库存方式

上一篇: 暂无

相关文章

zenglMall v0.3.0 采用前后端完全分离模式

zenglMall v0.2.0 增加商品库存,采用付款后减库存方式

zenglMall v0.4.0 增加商品属性和商品规格