苹果宣布 2017 年 1 月 1 日开始所有上架的应用必须启用 ATS 安全传输功能。ATS 要求服务器必须支持传输层安全(TLS)协议 1.2 以上版本;证书必须使用 SHA256 或更高的哈希算法签名;必须使用 2048 位以上 RSA 密钥或 256 位以上 ECC 算法等等,不满足条件的证书,ATS 都会拒绝连接。查看具体要求
微信小程序进入开发公测阶段,同样要求提供 HTTPS 方式安全连接。
证书类型对比
DV SSL 域名验证型 | OV SSL 组织验证型 | EV SSL 扩展验证型 | ||
申请条件 | 申请对象 | 个人 / 企业 | 企业 | 企业 |
证明域名所有权 | √ | √ | √ | |
价格 | 免费 / 低 | 中 | 高 | |
提交审核 | 提交资料 | 邮箱、姓名、手机等 | DV 所需资料以及: 组织机构资料(企业执照)等 | OV 所需资料以及: 法律相关文件 |
审核时间 | 自动审核 1小时内 | 人工审核 3~5工作日 | 人工审核 3~5工作日 | |
可信标识 | 地址栏挂锁 | √ | √ | √ |
地址栏公司名称 | × | × | √ | |
绿色地址栏(IE) | × | × | √ | |
保护范围 | 单域名 | √ | √ | √ |
多域名 | √ | √ | √ | |
通配符/泛域名 | √ | √ | × | |
推荐用途 | 个人及小型网站 | 一般组织或中小型企业 | 金融、电商、大型企业或受信组织 |
* 表格中,单域名指单个子域名(如:123.abc.com),多域名指多个子域名(可以不属于同一个顶级域名,如:123.abc.com / 456.abc.com / 456.def.com),通配符指单个顶级域名的所有子域名(如:*.abc.com)。
* 若 https 的网页中包含 http 资源,则地址栏的锁可能会消失。
* 浏览器上点击地址栏的锁可以查看具体的证书详情。
证书颁发机构(CA)
机构 | 简介 | 谁在用 |
Let's Encrypt | 公益组织,免费,每 90 天续约 | 越来越多的中小网站 |
Symantec | 赛门铁克 | 百度、支付宝、阿里云、Apple(EV)、微软 |
GlobalSign | 公众信任服务行业的领头羊 | 淘宝、天猫(OV)、阿里云、京东(OV) |
GeoTrust | 全球第二大? | 微信 |
Comodo | 价格实惠 | |
GoDaddy | 全球互联网域名注册商 | |
DigiCert | 不颁发 DV,在非 DV 市场份额中是老大 | Facebook、Twitter(EV)、Mozilla(EV)、GitHub(EV)等 |
沃通 WoSign | 国产 | 360搜索(EV) |
Other |
* 赛门铁克收购了 VeriSign,VeriSign 又收购了 GeoTrust,所以 Symantec 和 GeoTrust 在 SSL 认证服务上就不知道是什么关系了,请自行百度。
* 代理商也可以购买,或许价格优惠,或许申请方便,如:阿里云、腾讯云等,甚至上淘宝购买。
* 查看:Netcraft 统计的市场份额
* 网上说使用 Let's Encrypt 的 SSL 在遇到国内第三方 DNS 解析服务时会超时,我认为只是在申请证书时偶尔超时(DNS query timed out),重试即可,证书部署后访问网站时应该没有问题,请知情者告知实情。
下一步
最近做到微信语音时用到,将微信的媒体文件(.amr)下载到自己服务器上,并转码为 .mp3 格式。
下载 FFmpeg for Windows,放在网站目录下,譬如:网站目录/bin/ffmpeg/ffmpeg.exe
转换代码
string amr = HttpRuntime.AppDomainAppPath + media.sLocalPath.Substring(1).Replace("/", @"\"); string mp3 = amr + ".mp3"; string ffmpeg = HttpRuntime.AppDomainAppPath + @"bin\ffmpeg\ffmpeg.exe"; Process p = new Process(); p.StartInfo.FileName = ffmpeg; p.StartInfo.Arguments = $@"-y -i {amr} -ar {16000} {mp3}"; p.Start(); p.WaitForExit(); p.Close();
-y 若有提示用户选择,默认“是”
-ar 采样数,微信的 amr 是 8000 Hz,如果不加这个参数转换后的 mp3 的也是 8000 Hz,但是音质下降严重,所以设成 16000 Hz 才勉强保持了音质。
WaitForExit() 是等待转码完毕才继续往下执行。
最小化安装 CentOS 6.4,配置网络
一条一条执行:
yum -y update
yum -y install wget
yum -y install vim
yum -y install screen
screen -S lnmp
安装 LNMP:http://lnmp.org/install.html
下载安装一条龙
不要升级各软件,以防不测
PHP 防跨站:执行一段命令,替换 vhost.sh 文件,以后添加网站就会自动添加 HOST 防跨站、跨目录的配置
更改数据库路径(/home/mysql/var)
http://bbs.vpser.net/thread-1558-1-1.html 第20条
端口(为了 pureftpd 能安装成功,还是不要改端口了)
防火墙加端口
vim /etc/sysconfig/iptables
service iptables restart
FreeTDS:使 php 支持 mssql
安装 PureFTPd
添加FTP用户时,UID和GID必须>1000,譬如添加一个xWeb组和用户:
groupadd -g 2000 xWeb
useradd -u 2000 -g xWeb -s /sbin/nologin -M userDefault
useradd -u 2001 -g xWeb -s /sbin/nologin -M userFang
useradd -u 2002 -g xWeb -s /sbin/nologin -M user2
useradd -u 2003 -g xWeb -s /sbin/nologin -M user3
然后 chown xUser:xWeb -R /home/wwwroot/网站目录
这样PHP木马就不能上传。非www用户每站一个,防止跨站
要写入的目录chown为www用户,这样PHP能创建目录及上传文件,允许公共写入,使FTP能操作写入(未验证PHP创建的新目录FTP有没有写入权限,即继承),所有要写入的目录必须 deny all
chown userFang:xWeb -R /home/wwwroot/fang.eyuyao.com/
chown www:www -R /home/wwwroot/fang.eyuyao.com/uploads/
chown www:www -R /home/wwwroot/fang.eyuyao.com/eyy/src/
chown www:www -R /home/wwwroot/fang.eyuyao.com/index/Runtime/
chown www:www -R /home/wwwroot/fang.eyuyao.com/admin/Runtime/
—————————————————————————————————
#设置目录不允许执行PHP(其实是使符合正则的路径不可读)
#找到网站的 .conf 配置文件,在 location ~ .*\.(php|php5)?$ 的上面插入:
location ~ /upload/.*\.(php|php5)?$
{
deny all;
}
#支持 ThinkPHP(使用 rewrite)
location ~ /index\.php/.*$
{
if (!-e $request_filename) {
rewrite ^/index\.php(/.*)$ /index.php?s=$1 last;
break;
}
}
—————————————————————————————————
重启 LNMP /root/lnmp restart
重启 MySQL /etc/init.d/mysql restart
重启 PureFTPd /root/pureftpd restart
查看 Nginx 版本 nginx -V
查看 MySQL 版本 mysql -V
查看 PNP 版本 php -v
查看 Apache 版本 httpd -v
查内存 cat /proc/meminfo
php.ini vim /usr/local/php/etc/php.ini
iptables 路径 /etc/sysconfig/iptables
MySQL 配置文件 vim /etc/my.cnf
添加网站 /root/vhost.sh
添加ProFTPd用户 /root/proftpd_vhost.sh
—————————————————————————————————
遇到问题:
中文URL问题解决方案,FTP用强制UTF-8,单个文件传。否则在win下打包的zip在linux下解压后,编码不是utf-8,导致打开URL 404
能用记事本打开的文件若包含中文,应另存为 utf-8 编码。
ThinkPHP 项目修改配置文件后,必须删除 /index/Runtime/* 缓存文件!!!
检查目前硬盘状态:fdisk -l
关机并插入新硬盘
对新硬盘分区:fdisk /dev/sdb (假设新硬盘为sdb)
m 显示命令菜单
d 删除一个分区
n 创建一个分区(e 扩展分区;p 主分区)
t 改变分区ID
q 不保存退出
w 保存退出
对新硬盘格式化:mkfs.ext4 /dev/sdb1(这个数字是分区时指定的,fdisk -l 中可查)
创建挂载目录:mkdir /挂载点
挂载分区:mount /dev/sdb1 /挂载点
(卸载分区:umount /dev/sdb1)
开机自动挂载:
vi /etc/fstab
--------------------------------------------------------------------------------------------
相关命令:
df -hT 查看已挂载设备的用量及类型
fdisk -l 查看分区类型等
free -m 查看内存及 swap 用量
--------------------------------------------------------------------------------------------
若在两块硬盘的电脑上重新安装系统,则默认建立 LVM 卷组,如下:
设备 大小 挂载点/RAID/卷 类型 格式
LVM 卷组
VolGroup 390540
lv_root 51200 / ext4 √
lv_home 335452 /home ext4 √
lv_swap 3888 swap √
硬盘驱动器
sda
sda1 500 /boot ext4 √
sda2 152126 VolGroup physical volume (LVM) √
sdb
sdb1 238417 VolGroup physical volume (LVM) √
总结:VolGroup 逻辑卷组的大小是 sda2 和 sdb1 大小之和。(因为 sda2 和 sdb1 的挂载点都是 VolGroup)
VolGroup 视为一个硬盘整体再分成 lv_root、lv_home、lv_swap 等分区。
交换分区也在逻辑卷内,其类型是 swap。
除了 swap 和 LVM 类型,其它分区基本是 ext4 类型了。
若在仅有一块硬盘的电脑上重新安装系统,也是按这种格局分区,
只是硬盘驱动器那只能看到一块硬盘,且 VolGroup 的大小就是那个类型为 LVM 的分区的大小。
前言
最近在学习Web Api框架的时候接触到了async/await,这个特性是.NET 4.5引入的,由于之前对于异步编程不是很了解,所以花费了一些时间学习一下相关的知识,并整理成这篇博客,如果在阅读的过程中发现不对的地方,欢迎大家指正。
同步编程与异步编程
通常情况下,我们写的C#代码就是同步的,运行在同一个线程中,从程序的第一行代码到最后一句代码顺序执行。而异步编程的核心是使用多线程,通过让不同的线程执行不同的任务,实现不同代码的并行运行。
前台线程与后台线程
关于多线程,早在.NET2.0时代,基础类库中就提供了Thread实现。默认情况下,实例化一个Thread创建的是前台线程,只要有前台线程在运行,应用程序的进程就一直处于运行状态,以控制台应用程序为例,在Main方法中实例化一个Thread,这个Main方法就会等待Thread线程执行完毕才退出。而对于后台线程,应用程序将不考虑其是否执行完毕,只要应用程序的主线程和前台线程执行完毕就可以退出,退出后所有的后台线程将被自动终止。来看代码应该更清楚一些:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace ConsoleApp { class Program { static void Main(string[] args) { Console.WriteLine("主线程开始"); //实例化Thread,默认创建前台线程 Thread t1 = new Thread(DoRun1); t1.Start(); //可以通过修改Thread的IsBackground,将其变为后台线程 Thread t2 = new Thread(DoRun2) { IsBackground = true }; t2.Start(); Console.WriteLine("主线程结束"); } static void DoRun1() { Thread.Sleep(500); Console.WriteLine("这是前台线程调用"); } static void DoRun2() { Thread.Sleep(1500); Console.WriteLine("这是后台线程调用"); } } }
运行上面的代码,可以看到DoRun2方法的打印信息“这是后台线程调用”将不会被显示出来,因为应用程序执行完主线程和前台线程后,就自动退出了,所有的后台线程将被自动终止。这里后台线程设置了等待1.5s,假如这个后台线程比前台线程或主线程提前执行完毕,对应的信息“这是后台线程调用”将可以被成功打印出来。
Task
.NET 4.0推出了新一代的多线程模型Task。async/await特性是与Task紧密相关的,所以在了解async/await前必须充分了解Task的使用。这里将以一个简单的Demo来看一下Task的使用,同时与Thread的创建方式做一下对比。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Web; using System.Threading; using System.Threading.Tasks; namespace TestApp { class Program { static void Main(string[] args) { Console.WriteLine("主线程启动"); //.NET 4.5引入了Task.Run静态方法来启动一个线程 Task.Run(() => { Thread.Sleep(1000); Console.WriteLine("Task1启动"); }); //Task启动的是后台线程,假如要在主线程中等待后台线程执行完毕,可以调用Wait方法 Task task = Task.Run(() => { Thread.Sleep(500); Console.WriteLine("Task2启动"); }); task.Wait(); Console.WriteLine("主线程结束"); } } }
首先,必须明确一点是Task启动的线程是后台线程,不过可以通过在Main方法中调用task.Wait()方法,使应用程序等待task执行完毕。Task与Thread的一个重要区分点是:Task底层是使用线程池的,而Thread每次实例化都会创建一个新的线程。这里可以通过这段代码做一次验证:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Web; using System.Threading; using System.Threading.Tasks; namespace TestApp { class Program { static void DoRun1() { Console.WriteLine("Thread Id =" + Thread.CurrentThread.ManagedThreadId); } static void DoRun2() { Thread.Sleep(50); Console.WriteLine("Task调用Thread Id =" + Thread.CurrentThread.ManagedThreadId); } static void Main(string[] args) { for (int i = 0; i < 50; i++) { new Thread(DoRun1).Start(); } for (int i = 0; i < 50; i++) { Task.Run(() => { DoRun2(); }); } //让应用程序不立即退出 Console.Read(); } } }
运行代码,可以看到DoRun1()方法每次的Thread Id都是不同的,而DoRun2()方法的Thread Id是重复出现的。我们知道线程的创建和销毁是一个开销比较大的操作,Task.Run()每次执行将不会立即创建一个新线程,而是到CLR线程池查看是否有空闲的线程,有的话就取一个线程处理这个请求,处理完请求后再把线程放回线程池,这个线程也不会立即撤销,而是设置为空闲状态,可供线程池再次调度,从而减少开销。
Task<TResult>
Task<TResult>是Task的泛型版本,这两个之间的最大不同是Task<TResult>可以有一个返回值,看一下代码应该一目了然:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Web; using System.Threading; using System.Threading.Tasks; namespace TestApp { class Program { static void Main(string[] args) { Console.WriteLine("主线程开始"); Task task = Task.Run(() => { Thread.Sleep(1000); return Thread.CurrentThread.ManagedThreadId.ToString(); }); Console.WriteLine(task.Result); Console.WriteLine("主线程结束"); } } }
Task<TResult>的实例对象有一个Result属性,当在Main方法中调用task.Result的时候,将等待task执行完毕并得到返回值,这里的效果跟调用task.Wait()是一样的,只是多了一个返回值。
async/await 特性
经过前面的铺垫,终于迎来了这篇文章的主角async/await,还是先通过代码来感受一下这两个特性的使用。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Web; using System.Threading; using System.Threading.Tasks; namespace TestApp { class Program { static void Main(string[] args) { Console.WriteLine("-------主线程启动-------"); Task task = GetLengthAsync(); Console.WriteLine("Main方法做其他事情"); Console.WriteLine("Task返回的值" + task.Result); Console.WriteLine("-------主线程结束-------"); } static async Task GetLengthAsync() { Console.WriteLine("GetLengthAsync Start"); string str = await GetStringAsync(); Console.WriteLine("GetLengthAsync End"); return str.Length; } static Task GetStringAsync() { return Task.Run(() => { Thread.Sleep(2000); return "finished"; }); } } }
首先来看一下async关键字。async用来修饰方法,表明这个方法是异步的,声明的方法的返回类型必须为:void或Task或Task<TResult>。返回类型为Task的异步方法中无需使用return返回值,而返回类型为Task<TResult>的异步方法中必须使用return返回一个TResult的值,如上述Demo中的异步方法返回一个int。而返回类型可为void,则是为了和事件处理程序兼容,比如下面的示例:
public Form1() { InitializeComponent(); btnDo.Click += Down; } public async void Down(object sender, EventArgs e) { btnDo.Enabled = false; string str = await Run(); labText.Text = str; btnDo.Enabled = true; } public Task Run() { return Task.Run(() => { Thread.Sleep(5000); return DateTime.Now.ToString(); }); }
再来看一下await关键字。await必须用来修饰Task或Task<TResult>,而且只能出现在已经用async关键字修饰的异步方法中。
通常情况下,async/await必须成对出现才有意义,假如一个方法声明为async,但却没有使用await关键字,则这个方法在执行的时候就被当作同步方法,这时编译器也会抛出警告提示async修饰的方法中没有使用await,将被作为同步方法使用。了解了关键字async\await的特点后,我们来看一下上述Demo在控制台会输入什么吧。
输出的结果已经很明确地告诉我们整个执行流程了。GetLengthAsync异步方法刚开始是同步执行的,所以"GetLengthAsync Start"字符串会被打印出来,直到遇到第一个await关键字,真正的异步任务GetStringAsync开始执行,await相当于起到一个标记/唤醒点的作用,同时将控制权放回给Main方法,"Main方法做其他事情"字符串会被打印出来。之后由于Main方法需要访问到task.Result,所以就会等待异步方法GetLengthAsync的执行,而GetLengthAsync又等待GetStringAsync的执行,一旦GetStringAsync执行完毕,就会回到await GetStringAsync这个点上执行往下执行,这时"GetLengthAsync End"字符串就会被打印出来。
当然,我们也可以使用下面的方法完成上面控制台的输出。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Web; using System.Threading; using System.Threading.Tasks; namespace TestApp { class Program { static void Main(string[] args) { Console.WriteLine("-------主线程启动-------"); Task task = GetLengthAsync(); Console.WriteLine("Main方法做其他事情"); Console.WriteLine("Task返回的值" + task.Result); Console.WriteLine("-------主线程结束-------"); } static Task GetLengthAsync() { Console.WriteLine("GetLengthAsync Start"); Task task = Task.Run(() => { string str = GetStringAsync().Result; Console.WriteLine("GetLengthAsync End"); return str.Length; }); return task; } static Task GetStringAsync() { return Task.Run(() => { Thread.Sleep(2000); return "finished"; }); } } }
对比两种方法,是不是async\await关键字的原理其实就是通过使用一个线程完成异步调用吗?答案是否定的。async关键字表明可以在方法内部使用await关键字,方法在执行到await前都是同步执行的,运行到await处就会挂起,并返回到Main方法中,直到await标记的Task执行完毕,才唤醒回到await点上,继续向下执行。更深入点的介绍可以查看文章末尾的参考文献。
async/await 实际应用
微软已经对一些基础类库的方法提供了异步实现,接下来将实现一个例子来介绍一下async/await的实际应用。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Web; using System.Threading; using System.Threading.Tasks; using System.Net; namespace TestApp { class Program { static void Main(string[] args) { Console.WriteLine("开始获取博客园首页字符数量"); Task task1 = CountCharsAsync("http://www.cnblogs.com"); Console.WriteLine("开始获取百度首页字符数量"); Task task2 = CountCharsAsync("http://www.baidu.com"); Console.WriteLine("Main方法中做其他事情"); Console.WriteLine("博客园:" + task1.Result); Console.WriteLine("百度:" + task2.Result); } static async Task CountCharsAsync(string url) { WebClient wc = new WebClient(); string result = await wc.DownloadStringTaskAsync(new Uri(url)); return result.Length; } } }
参考文献:<IIIustrated C# 2012> 关于async/await的FAQ 《深入理解C#》
问题描述:
当我们的界面需要在程序运行中不断更新数据时,当一个textbox的数据需要变化时,为了让程序执行中不出现界面卡死的现像,最好的方法就是多线程来解决
一个主线程来创建界面,使用一个子线程来执行程序并更新主界面
这样就不会出现卡死的现像了
这肯定是没有问题的,
但是为什么在使用的过程中一样会有很多地方会出现卡死呢,而且有用户跟我说是我的Httphelper类的问题,其实不是,而且我再次声明我的Httphelper类跟多线程并没有关系。不要在诬赖我了哦。
这个问题其实也困或了我很久,但是今天终于解决了,而且我发现很多人有这样的问题,所以我分享一个例子方便大家参考吧。
先来看看我的界面
当我单击
开始执行后
这个时候界面是不会卡死的,
只是数据在不断的更新
下面看看我的代码
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Threading; namespace WindowsFormsApplication3 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } //创建一个委托,是为访问TextBox控件服务的。 public delegate void UpdateTxt(string msg); //定义一个委托变量 public UpdateTxt updateTxt; //修改TextBox值的方法。 public void UpdateTxtMethod(string msg) { richTextBox1.AppendText(msg + "\r\n"); richTextBox1.ScrollToCaret(); } //此为在非创建线程中的调用方法,其实是使用TextBox的Invoke方法。 public void ThreadMethodTxt(int n) { this.BeginInvoke(updateTxt, "线程开始执行,执行" + n + "次,每一秒执行一次"); for (int i = 0; i < n; i++) { this.BeginInvoke(updateTxt, i.ToString()); //一秒 执行一次 Thread.Sleep(1000); } this.BeginInvoke(updateTxt, "线程结束"); } //开启线程 private void button1_Click(object sender, EventArgs e) { Thread objThread = new Thread(new ThreadStart(delegate { ThreadMethodTxt(Convert.ToInt32(textBox1.Text.Trim())); })); objThread.Start(); } private void Form1_Load_1(object sender, EventArgs e) { //实例化委托 updateTxt = new UpdateTxt(UpdateTxtMethod); } } }
就这些代码,大家看注释应该就明白一点了,
主要是使用一个委托来更新界面的richTextBox1
这样写是肯定没有问题的,而且在我其它的更高级一点的例子里也是这么写的
C#多线程|匿名委托传参数|测试网站压力--升级版
http://www.sufeinet.com/thread-13-1-1.html
上面的文件大家可以做为参考
那问题现在那里呢,其实就出在这一句上
this.BeginInvoke(updateTxt, "线程结束");
大家也许已经发现了,我是这样写的,而不是
updateTxt("线程结束");
这样来直接在子线程中使用,
我相信有很多同志都是这样写的,其实错就错在这里
如果直接使用
updateTxt("线程结束");
大家想一下应该就明白了,
updateTxt是在主线程创建的,而我们在子线程中直接使用,运行的数据多了,就会出现卡死,这是界面信息堵死的原因,
所以就算是委托也不能直接在子线程中使用,而是要使用BeginInvoke方法来调用这个委托
这样才不会出现卡死的现像。
问题就解决了。
大家支持一下哦
下面是我的源码提供给大家下载吧
WindowsFormsApplication3.zip (49.65 KB)
wxy0510:经过用户表优化后,论坛不活跃用户将被转至存档表。这将意味着论坛在常规调用、查询都不会遍历这些存档表的数据。这也大大提高服务器工作效率,减少服务器负载。当然处于存档表的用户在执行一次登录操作后,数据会重新转入主表。
xoyozo:这个登录是指论坛本身的用户登录,如果用第三方客户端 App 登录一般是不起作用的(除非 App 考虑到了这个情况)
common_member 是主表,common_member_archive 是存档表,这两个表的用户数相加即为 UC 用户表 ucenter_members 中的用户总和。
海鲜大部分是冷冻包装产品,小部分是散装称重产品,与超市类似,应选择“商超版”,除了钱箱、扫描枪,还应接电子秤。
触摸屏
不用记键盘快捷键,商品少的话直接点选,连扫码、输码、搜索都省了
好用的收银软件
见下方表格
称重一体
散装商品方便取重
Windows 7 及以上版本 或 安卓 操作系统
安心连网,用于支付宝/微信收款,方便连锁店共享数据。选择 Windows 的话建议选到 win10,因为 win7 将在 2020 年停止支持;新的收银机基本用的安卓,可能因为各种成本低吧,不过听说安卓收银机会卡顿,类似于安卓手机的操作感,核心数和内存跟PC也不是一个概念。
固态硬盘
Windows 建议选固态,开机快;安卓是闪存
销售过程中任何时间使用会员卡,而不是必须先扫会员卡,再扫商品
标签打印机,支持打印价格签(不干胶热敏纸条码),称重商品需要贴上即时生成的条码
支持扫描枪或扫码器扫描客户手机上的支付宝、微信(动态)付款码,以规避客户使用支付成功的截图来欺骗。需要一些配置,提现扣手续费。
支持拼音简码销售
选购步骤 先选择适合自己的收银软件,再选择配置和外观,当然要考虑外接设备的兼容性(电子秤同步称重、软件开钱箱、是否需要标签打印机或后厨打印机、扫描枪是否需要支持支付宝微信 等等)。
注:标签打印机不同于小票打印机,前者用于打印临时标签,可以包含名称、价格、重量、条码等信息,贴于散装称重商品,需选购;后者打印小票给顾客提供购买商品的明细,一般收银机自带。
附:几大收银软件功能对比(从商超的角度分析)注:部分功能可能因配置不同而不同,具体应咨询客服。
二维火 | 来钱快 | 美团收银 | |
版本 | 餐饮版 商超版 奶茶版:适用连锁店 | 基础版:适用餐饮 零售版:适用超市 专业版:适用连锁餐厅 | |
操作系统 | 安卓 | 安卓 | 安卓 |
适配触摸屏 | √ | √ | √ |
扫描商品条码、支付二维码 | √ | √ | √ |
对接外卖平台 | 美团外卖、饿了么、百度外卖 | 美团外卖、饿了么 | 美团外卖 |
手机下单 | √ | 免费微店、客户自助点餐 | 收银机点餐、顾客扫码点餐、服务员点餐 |
收银方式 | 聚合支付、支付宝、微信、现金 | 现金、支付宝、微信、银联、优惠券组合付款 | 现金、支付宝、微信、优惠券、会员、记账 |
会员功能 | 会员等级、积分、折扣、微信公众号营销 | 会员等级、充值、积分、折扣、俏销短信、报表 | 短信、储值、积分 |
支持手机 | 火掌柜App | 来钱快App 收银助手 | 美团管理APP |
报表 | √ | √ | √ |
自制条码(接标签打印机) | 商超版、奶茶版支持 | ||
盘点库存 | √ | 手机操作 | 电脑端 |
对接电子秤 | √ | 商超版支持 | |
连锁店管理 | √ | √ | 专业版支持 |
其它特点 | 排队叫号; 自主微信公众号粉丝运营; 小程序点餐 | 免费微信店铺,推广公众号 | 提高美团和大众点评店铺人气; 排队软件; 一店多收银台; |
售价 | 买收银机赠(零售 488 元),免费升级,连锁版每用户 599 元 | 零售版和基础版免费(买收银机赠),专业版收费 | |
相关链接 | 来钱快: 杭州萨宝科技: 软件和驱动: | 官方网站: 视频教程: http://shouyin.meituan.com/productGuide | |
我的评价 | 界面拟物还不错,具体没有深究 | 公司没有美团有名,但是入行比美团早,产品成熟,创新力强 | 可能更适合于奶茶店类型,对打包、外卖完美适用,有机会试用的话可以给出更客观的评价 |
本文资料整理于 2018 年 7 月
舟山海鲜捕鱼人品牌诚招代理,电话:13646674565(微信同号)
在开发微信中的网页时,会遇到一些域名相关的配置:
① 公众号设置 - 功能设置 - 业务域名
② 公众号设置 - 功能设置 - JS接口安全域名
③ 接口权限 - 网页授权获取用户基本信息
④ 商户平台 - 产品中心 - 开发配置 - JSAPI支付授权目录
⑤ 小程序 - 开发 - 开发设置 - 业务域名
⑥ 公众号开发 - 基本设置 - IP白名单
第①种 业务域名:相对不重要,只是用来禁止显示“防欺诈盗号,请勿支付或输入qq密码”提示框,可配置 3 个二级或二级以上域名(个人理解是“非顶级域名”,即填写了 b.a.com 的话,对 c.b.a.com 不起作用,待测)。
我们网站的域名和公众号是没有绑定关系的,那么你在打开一个(可能是朋友分享的)网页时,跟哪个公众号的配置去关联呢,答案是 JS-SDK。
测试结果:由于一个月只有3次修改机会,这次先测二级域名,有效;再测顶级域名,有效;删除所有,仍有效。所以应该是缓存作用。过几天再试,然后下个月先测顶级域名,来确定直接填写顶级域名是否对所有二级域名有效。
第②种 JS接口安全域名:是配置所配置的域名下的网页可调用 JS-SDK。可配置 5 个一级或一级以上域名(个人理解是“任何级域名”,即填写了 b.a.com 对 c.b.a.com 也有效,但对 c.z.a.com 无效,待测。如果我们拥有顶级域名对应网站的控制权(上传验证文件到网站根目录),直接填写顶级域名即可)。
使用 JS-SDK 的每个网页都必须注入配置信息(wx.config),而之前必须获取 jsapi_ticket,jsapi_ticket api 的调用次数非常有限,必须全局缓存。而获取 jsapi_ticket 之前必须先获取 access_token,同样需要全局缓存。所以,我们专门做个接口,功能是传入需要使用 JS-SDK 的网页的 url,输出 wx.config 需要用到的配置信息,来实现在不同网页(网站)使用 JS-SDK。
第③种 网页授权域名:这是已认证的服务号才能享有的特权,主要作用是获取用户在该服务号中的 openid 和 unionid。可配置 1 个 2 个回调域名(可填写任意级别的域名,但仅对该域名的网页(网站)有效,若填写了 a.com 对 b.a.com 是无效的)。
因此,如果我们需要在不同二级域名甚至不同顶级域名下的网页(以下称之为活动页面)实现用户授权,需要做一个统一的代理授权页面(回调域名当然是填写这个页面所在的域名),引导用户依次打开:微信授权页面 - 代理授权页面 - 活动页面,根据开发说明文档,具体实现如下:
当用户第一次打开活动页面时,引导打开微信授权页面(https://open.weixin.qq.com/connect/oauth2/authorize),其中参数 redirect_uri 指定回调地址,即代理授权页面地址,参数 state 指定活动页面地址。微信授权页面返回 code 和 state,code 作为换取网页授权 access_token 的票据。到这一步,本来可以由代理授权页面直接拿这个 code 去换取 access_token、openid 和 unionid 了,但是由于当前用户还在 302 重定向过程中,将这些信息带入到活动页面时势必导致信息泄露,所以这里将 code 追加到 state 指定的网址上后重定向到活动页面,活动页面拿到 code 再通过服务器端向代理授权页面所在服务器请求 openid 和 unionid,并将它们保存于 Session 中视为用户登录。这样,服务号的 appid 和 secret 也能得到保护。代理授权页面请求的微信服务器接口地址是 https://api.weixin.qq.com/sns/oauth2/access_token。
注:网页授权 access_token 不同于 JS-SDK 中使用的全局唯一接口调用凭据 access_token,没有请求次数限制。
流程既然通了,实现逻辑可以这样设定:
活动页面首先判断 Session 中是否有 openid 或 unionid,若有表示已授权登录;没有再判断地址栏是否有 code 参数,若有则调用代理授权页面所在服务器的接口,用 code 换 openid 和 unionid;没有则直接重定向到代理授权页面,带上 state。用 code 换 openid 和 unionid 时若成功则保存至 Session,若失败则仍然重定向到代理授权页面,带上 state,特别注意 state 中的活动页面地址确保没有 code 参数。
第④种 JSAPI支付授权目录:涉及到微信支付时用到,顾名思义是固定某一个网站内的某个目录,以“/”结尾。最多可添加 5 个。如果支付页面在目录 https://www.a.com/b/ 下,那么可以填写 https://www.a.com/b/ 或 https://www.a.com/(建议后者),暂未测试填写 https://a.com/ 会不会起作用。涉及支付安全,建议设置支付页面的最深一层不可写的目录,以防目录内被上传后门文件带来的安全隐患。
第⑤种 小程序业务域名:任何需要在小程序的 web-view 组件中打开的网页,都必须配置小程序业务域名,限制 20 个。该域名要求必须 https,可填入“任何级域名”(建议填顶级域名,即填写了 a.com 对 b.a.com 也有效。如果我们拥有顶级域名对应网站的控制权(上传验证文件到网站根目录),直接填写顶级域名即可)。
第⑥种 IP白名单:仅填写管理全局 access_token 的中控服务器的 IP。
总结:在上述自定义接口部署完成后,如果微信中的网页想获取用户的 unionid,则不需要配置域名,直接使用统一的代理授权即可;如果需要使用 JS-SDK 功能,如分享、上传等等,则需要配置 JS 接口安全域名;如果有表单,最好配置一下业务域名。
本文系个人经验总结,部分结果未经证实,欢迎指正!QQ:940534113
本文是一个初学者对苹果开发证书的理解,有一定局限性,不适合所有苹果开发者参考,欢迎批评指正。
发布 iOS 应用主要有三块内容:Certificates, Identifiers & Profiles
Certificates(证书):是用来给应用程序签名的
Identifiers(标识符):一个应用对应一个 ID,相当于应用程序的身份证
Provisioning Profiles(描述文件):它将证书、标识符、设备结合起来,形成一个描述文件,让 Xcode 知道需要打一个怎么样的 .ipa 包
如何创建证书
创建 App ID 就不详说了,不要使用带通配符的名称。值得一提的是,如果要使用推送,必须勾选“Push Notifications”,其它功能按需勾选。
创建证书,需要用到 Mac 电脑上的“钥匙串访问”来生成一个“本地证书”(CSR 文件):
打开“钥匙串访问” - 证书助理 - 从证书颁发机构请求证书 - 保存到磁盘
然后就可以拿这个本地证书去 Apple Developer 里生成“开发证书”或“发布证书”(CER 文件)。打包证书和推送证书生成过程类似,区别是选择“App Store and Ad Hoc”还是“Apple Push Notification service SSL”。下载后添加到钥匙串就完成了。
添加测试设备就不提了。
然后就是描述文件,同样有开发和发布两种。开发主要是用于 Xcode 环境中;发布中还分两种:App Store 是用于正式上线的,Ad Hoc 是用于测试设备的。下一步选择 App ID,下一步选择证书,然后给 Profile 取个能够分辨的名字,生成的是一个 mobileprovision 文件。
提供证书文件给第三方打包平台
一般需要提供“iOS Distribution 证书”、“推送证书”、“Ad Hoc”和“App Store”类型的 Profiles、App ID 以及 iTunes Connect 中的 Apple ID(不是指登录的邮箱,是指在 iTunes Connect 中创建的 App 在商店中的 ID)。
证书其实跟 App ID 不是一一对应的关系,多个 App 是可以使用同一个证书的。可以单独创建,到期互不影响。Download 后是 .cer 格式,用“钥匙串”工具可以导出 .p12 证书并设置密码。
如果由于命名无序或创建错误导致无法分辨上架中的 App 用的是哪个证书,哪个描述文件,那么,首先要知道这个 App ID 是多少(就是 iTunes Connect 中所谓的“套装 ID”),然后在 Provisioning Profiles 中挨个查看对应的 App ID,点击“Edit”还能看到使用的证书。
有些平台需要 .pem 文件,是由 .p12 文件通过一个命令转化而来的,具体可以百度。
证书过期怎么办
创建一个呗,然后描述文件 Edit,选择新的证书。
推送证书过期怎么办
在 App ID 里 Edit 就能查看 Push Notifications 项的 SSL Certificate 是否过期。如果过期需要重新创建证书,可以直接在 App ID 的 Edit 里快捷创建(需要“钥匙串”配合)。
不用重新提交 App Store 审核
换一台电脑,证书要怎么导过去
这个问题适用于自己使用 Xcode 开发应用的情况,暂时问一下百度吧。
注:本文部分总结来自百度,未经证实。
Apple Push Services 和 APNs Production iOS 这两种类型有什么区别?