科技文章

  • 发布Electron App到Mac App Store

    原文链接,此文在原文基础上有补充。

    此文章基于Catalina新版发布规则撰写。

    你有两种选择来发布适用于MacOS的Electron App。一种是直接发布(请参阅对Electron App进行公证),或通过Mac App Store(MAS)发布。通过MAS分发的所有应用程序都必须进行沙盒处理(sandbox),这意味着除已批准的权利(entitlements)外,它们完全独立。Mac的Catalina OS于2019年10月发布,添加了许多影响沙盒应用程序的更改,导致许多现有的Electron应用程序崩溃并拒绝了新应用程序。 要使基本应用程序成功提交以供审核并批准在Mac App Store上分发,您需要执行以下步骤。

    1. 从已经开发完成的Electron App开始

    必须使用Electron版本8.0.2或更高版本(或修补版本6.1.7和5.0.13),因为早期版本使用Apple不再允许的私有API。 Electron应该是dev(或global)依赖,而不是production依赖。

    npm install -D electron

    2. 苹果开发者账号

    必须要有苹果开发者账号,需要付费600多一年。在developer.apple.com注册。

    3. 创建两个签名证书

    每个证书创建需要两步。

    3.1 3rd Party Mac Developer Application 证书

    3.1.1 请求证书

    • 打开钥匙串访问(位于 应用程序 > 实用工具)
    • 点击菜单 > 证书助理 > 从证书颁发机构请求证书
    • 在弹出的证书助理窗口中,填写用户电子邮件地址
    • 填写常用名称(例如3rd Party Mac Developer
    • CA电子邮件地址留空
    • 选择存储到磁盘,点击继续
    • 选择存储目录后,会存储文件名CertificateSigningRequest.certSigningRequest

    3.1.2 导入证书到苹果开发者账号

    • 访问https://developer.apple.com/account > Certificates, IDs & Profiles > + (to add a new certificate) > Mac App Distribution > continue > Choose File > 选择刚刚生成的文件(CertificateSigningRequest.certSigningRequest) > continue > Download > 双击下载的文件(mac_app.cer),会自动加载到钥匙串。
    • 注意:在开发者账号网站中显示的名称是Mac App Distribution,加载到钥匙串之后显示的名称是3rd Party Mac Developer Application: {name} (XXXXXXXXXX),它们是相同的证书。
    • 删除CertificateSigningRequest.certSigningRequest文件。

    3.2 3rd Party Mac Developer Installer 证书

    大致重复3.1中的流程

    3.2.1 请求证书

    • 打开钥匙串访问(位于 应用程序 > 实用工具)
    • 点击菜单 > 证书助理 > 从证书颁发机构请求证书
    • 在弹出的证书助理窗口中,填写用户电子邮件地址
    • 填写常用名称(例如3rd Party Mac Developer
    • CA电子邮件地址留空
    • 选择存储到磁盘,点击继续
    • 选择存储目录后,会存储文件名CertificateSigningRequest.certSigningRequest

    3.2.2 导入证书到苹果开发者账号

    • 访问https://developer.apple.com/account > Certificates, IDs & Profiles > + (to add a new certificate) > Mac Installer Distribution > continue > Choose File > 选择刚刚生成的文件(CertificateSigningRequest.certSigningRequest) > continue > Download > 双击下载的文件(mac_installer.cer),会自动加载到钥匙串。
    • 注意:在开发者账号网站中显示的名称是Mac Installer Distribution,加载到钥匙串之后显示的名称是3rd Party Mac Developer Installer: {name} (XXXXXXXXXX),它们是相同的证书。
    • 删除CertificateSigningRequest.certSigningRequest文件。

    查看证书

    • 在钥匙串中查看证书:打开钥匙串访问,点击“登录”,点击“我的证书”,刚刚导入的两个证书应该在列表中,名称是3rd Party Mac Developer Application: {name} (XXXXXXXXXX) 。个人开发者的name就是你的名字。
    • 在苹果开发者账号网站上查看:https://developer.apple.com/account/ > Certificates, IDs, & Profiles menu > Certificates menu > Mac App Distribution and Mac Installer Distribution 两种类型的证书在列表中可见。

    证书备份

    注意:如果更换了开发用的Mac电脑,需要备份证书再导入新设备才可以使用本次生成的证书,否则只能重新生成,之前导入到苹果开发者网站的证书下载导入钥匙串之后会显示缺少私钥,而且网站上可保留的证书数量很有限,想添加多的,只能先revoke以前的。备份的证书需要选择p12格式,包含公钥和私钥,可以设置密码保护。把备份的证书妥善保管,以备更换设备或者切换设备使用。备份操作在证书列表右键,选择导出,按操作提示执行即可。

    4. 创建 App ID

    • Go to your developer account – developer.apple.com > Certificates, IDs & Profiles > Identifiers > + (to add a new id) > select App IDs > continue button.
    • On the Register App ID page:
      • Platform: MacOS
      • Bundle ID: Explicit. Apple recommends using a reverse-domain name style string (i.e., com.domainname.appname). This does not need to correspond with an actual website. This will need to match the appId property in the package.json file.
      • When done click continue, review it, then click register.

    5. 在开发者账号中新建App

    引用:https://help.apple.com/app-store-connect/
    官方文档: help.apple.com/app-store-connect/#/dev2cd126805

    此部分稍后再写,也有很多注意事项。

    6. Create an icon set

    参考:
    https://www.electron.build/icons
    https://developer.apple.com/design/human-interface-guidelines/macos/icons-and-images/app-icon

    此部分稍后再写

    7. package.json文件

    斜体字的值需要更改为你的信息。

    { 
      "name": "AppName",  
      "version": "1.0.0", 
      ...
      "author": "AuthorName", 
      "build": {  
        "appId": "com.companyname.appname",
        "productName": "App Name with spaces & special characters", 
        "buildVersion": "1",
        "copyright": "Copyright © 2020 Developer/Company Name", 
        "mac": {  
          "category": "public.app-category.categoryName", 
          "icon": "build/icon.icns",  
          "target": "mas",  
          "hardenedRuntime": false, 
          "entitlements": "build/entitlements.mas.plist", 
          "entitlementsInherit": "build/entitlements.mas.inherit.plist",  
          "provisioningProfile": "MacAppStore.provisionprofile",  
        } 
      } 
    } 

    参考文档:

    一些注意事项:

    • nameproductNamename将用作您应用在用户计算机上的显示名称。它不能包含空格或特殊字符。如果要在name中使用空格或特殊字符,请在build键中添加一个productName键。 您可以设置其名称,使其显示在Mac App Store的appstoreconnect.apple.com上。
    • versionbuildVersion:package.json中的version应与appstoreconnect.apple.com上的应用程序的版本号相对应。如果您将应用程序上传到appstoreconnect,但在决定将其提交审核之前进行更改,则无法删除上传的内容。此时需要设置不同的buildVersion,重新构建之后上传。
    • appId:使用之前在开发者账号网站中创建的App ID
    • category键应与您在appstoreconnect.apple.com中为App设置的category一致。List of possible categories
    • target需要设置为mas
    • hardenedRuntime需要设置为false。如果要将app分发到MAS之外,则可以将其设置为true。参考:developer.apple.com/documentation/security/hardened_runtime_entitlements

  • 电子发票解析结构化数据说明

    输出示例:

    {
      "发票代码": "031001900111",
      "发票号码": "96803177",
      "开票日期": "2020-06-04",
      "校验码": "15821 43303 58801 56335",
      "机器编号": "499099827029",
      "发票名称": "上海增值税电子普通发票",
      "购买方": {
        "名称": "上海少侠网络科技有限公司",
        "纳税人识别号": "91310230MA1K2X93X0",
        "地址、电话": "",
        "开户行及账号": ""
      },
      "密码": [
        "032821682*+*-9*36*6338+915<460/40351+19066>6434-5",
        "045292/12+/64-+352822/3>48<*",
        "796349569/>0*+74"
      ],
      "项目": [
        [
          "货物或应税劳务、服务名称",
          "*信息技术服务*信息系统增值服务",
          "*广告代理服务*广告代理费",
          "合计"
        ],
        [
          "规格型号"
        ],
        [
          "单位",
          "次",
          "次"
        ],
        [
          "数量",
          "1",
          "1"
        ],
        [
          "单价",
          "863.21",
          "80.19"
        ],
        [
          "金额",
          "863.21",
          "80.19",
          "¥943.40"
        ],
        [
          "税率",
          "6%",
          "6%"
        ],
        [
          "税额",
          "51.79",
          "4.81",
          "¥56.60"
        ]
      ],
      "合计": [
        "壹仟元整",
        "¥1000.00"
      ],
      "销售方": {
        "名称": "百度在线网络技术(北京)有限公司上海软件技术分公司",
        "纳税人识别号": "91310000772120643P",
        "地址、电话": "上海市嘉定区汇荣路468号2幢2层B区021-39005678",
        "开户行及账号": "招商银行上海分行曹家渡支行215081392810001"
      },
      "备注": [],
      "收款人": "百度",
      "复核": "",
      "开票人": "百度",
      "fileId": "dc20c6e07d858fdca9468055e05b54f8"
    }

    数据结构根节点为对象,对象的key基本上都是发票票面上的字段和字段值。

    开票日期统一格式化为YYYY-MM-DD格式,可能跟票面实际文本略有差异。

    购买方销售方下各有4个字段。

    密码字段是数组结构,解析正确时都有4个元素,按顺序对应发票票面密码区从上到下4行密码文本。

    项目字段是二维数组结构。表示“货物或应税劳务、服务名称”表格的内容,包括表头。项目一级数组有8个元素,对应表格的8列。二级数组中包含当前列的所有文本字段,包括表头。

    合计是价税合计一行的文本,是数组结构,一定包含大写文本和小写金额。

    备注是数组结构,包含发票票面备注区域的文本。

  • QQ音乐flac文件封面

    从QQ音乐下载的无损flac文件,在索尼等MP3播放设备上,是无法显示封面的,原因是QQ音乐把封面图片存储在了不正确的文件位置中,也就是非标准flac格式,所以索尼等设备使用标准flac格式就无法读取封面。

    提供一个免费工具可以自己重新设置flac文件封面:FLAC标签编辑器,欢迎大家使用

  • Node.js unable to verify the first certificate

    在node.js中使用request、superagent之类的请求https地址有可能会遇到

    unable to verify the first certificate

    这个问题是intermediate certificate导致的:the intermediate certificate wasn’t bundled along with the server certificate, you’ll need to fix that

    npm上有个package专门可以修复这个问题https://www.npmjs.com/package/ssl-root-cas,网上有很多方法是禁用ssl校验来规避问题,但是这种方法并不安全

    其实这个问题很有可能是服务器配置有疏漏导致的,如果是Apache2可以检查一下有没有配置SSLCertificateChainFile

  • php intval的一个小坑
    var_dump(intval('19.90' * 100)); // 1989
    var_dump(intval(floatval(1990))); // 1990

    计算得来的float 1990和直接声明得来的 float 1990,在intval取整的时候结果会不一样的

    跟float存储方式有关,一不小心可能就掉坑了。

    // 解决办法
    var_dump(intval(round('19.90' * 100))); // 1990