关于一些Golang包API设计的理解

有的API设计成通过传入引用来给调用者返回结果。

比如Gorm里Find 这个函数就需要传入指针来存放结果(假如你不传指针会直接报错)。

materials := make([]models.Material, 0)
var total int64
db := global.DB
db.Find(&materials).Count(&total)

仔细想来,应该是为了满足API的链式调用设计。链式调用需实例对象上的每个函数都返回且只能返回一个相同类型的指针,比如 Find 和 Count 的返回结果都是*gorm.DB。这样一来,传递结果只能通过函数接受一个指针对象才能实现函数的返回,Find(&materials).Count(&total)

类推到其他可以做链式调用的API,比如 Restry 之类,做赋值操作都要传一个指针进去。

umi-fabric 帮助写代码的工具

https://images.unsplash.com/photo-1554034483-04fda0d3507b?ixlib=rb-1.2.1&q=85&fm=jpg&crop=entropy&cs=srgb

一开始我对 lint 类工具不太感冒,觉得只要运行没问题就 OK 了,但是当入门之后想精进一步,lint 会帮你查漏补缺(群里的一个大佬提醒我的),帮你写出符合规范的代码。结合框架umi,查到了 umi-fabric ,一个包含 prettier,eslint,stylelint 的配置文件合集(我个人 stylelint 用的比较少,因为基本都是 tailwindcss )。

继续阅读umi-fabric 帮助写代码的工具

Gorm 关联关系彻底区分

https://images.unsplash.com/photo-1505686994434-e3cc5abf1330?ixlib=rb-1.2.1&q=85&fm=jpg&crop=entropy&cs=srgb

多对一 Belongs To 与 一对多 Has Many

// `User` 属于 `Company`,`CompanyID` 是外键
type User struct {
  gorm.Model
  Name      string
  CompanyID int
  Company   Company
}

type Company struct {
  ID   int
  Name string
}

一对多和多对一的区别在于语义侧重点不同。你可以说一个工人属于一个工厂也可以说一个工厂有多馆工人,前者是多对一,后者是一对多。

// Company 有多个 User,CompanyID 是外键
type User struct {
  gorm.Model
  Name      string
  CompanyID int
}

type Company struct {
  ID   int
  Name string
  Users User[]
}

在实际的操作中就是要看你是要查公司然后顺便知道这个公司下有多少工人,还是要知道一个工人对应的公司是什么。Gorm 提供了预加载 Preload 来做这一点 。所以多对一和一对多的区分前提是你知道自己要什么,然后定义它,最后用 Preload 来得到它。

继续阅读Gorm 关联关系彻底区分

独立开发者也需要原型工具

我一开始认为独立开发者是不需要原型工具的。原因有两个,一是原型工具上手需要成本。而独立开发者前端技术比较熟悉,比较容易构建一个原型出来。在这一点上,手绘草图的意义也大于原型工具。二是独立开发不需要沟通,原型做出来也没有意义。

但是在上手原型工具墨刀之后,我发现工具本身也在改变我的认知方式。首先,墨刀上手并不需要多少成本,反倒可以专注产品功能本身,而写代码无论怎样都可能陷入细节。其次分享功能的便利性让我学着去分享自己的创意,然后可以与用户进行沟通,可以在项目前期确定好功能点。

另外通过设计和编码进行分离,在设计和编码阶段都能更好的投入。若混合在一起,往往会因为不知道自己要什么而带来更大的成本。

Gin 路由默认重定向问题

有个问题很诡异,IOS Chrome 版本登陆会显示 401 未授权问题。经过定位,发现表面上是前端路由和后端路由不匹配才会出现。比如前端写着请求地址是 /api/users,后端路由是 /api/users/。但是在 PC 上却没有复现这个问题。PC 上 Chrome 根据 Gin 的 301 重定向到新的地址。IOS 没有办法看到,但是从后端日志看出应该是重定向再次访问的时候,手机浏览器没有带上 token 导致的,好像只是请求体的重定向。具体原因尚不清楚。

Gorm 关联语法解惑

Photo by Lerone Pieters on Unsplash

Gorm 关联语法与我的常识有冲突,导致尝试他的关联语法花了一些时间。最终结果如下

type CrawlResult struct {
	Crawler     Crawler   `gorm:"foreignKey:CrawlerId;references:CrawlerId"`
	CrawlerId   string    `json:"crawlerId" gorm:"column:crawler_id"`
}
type Crawler struct {
	CrawlerId string `gorm:"primaryKey;column:crawler_id"`
}

可以看到,在外键存在的表需要自己设定一个外键,这里是 CrawlerId,同时需要将对应结构体 Crawler 作为属性聚合在其中。这里 foreignKey 是指 CrawlResult 本体结构的属性 CrawlerId,references 指的是 Crawler 的外键。然后你在关联的时候可以直接这样做

db.Preload("Crawler").Find(&results)

这里 Preload 指的是外部结构名称。

总结

内部实体的外键要自己建立,然后用foreignKey关联才行。

产品随想

最近痴迷曾国藩传,试想读一些近代史相关的书籍,但是发现手头没有相关产品的。作为用户,我希望有一款可以输入一本书然后关联出所有相关知识点的工具,类似知识图谱。

这个工具比较适合 Web 大屏幕来展现。

暂时那么多。

产品护城河杂感

Photo by Zane Persaud on Unsplash

一个产品必定有其竞品,而一个优秀的产品则有他特有的被市场认可的模式(这个模式可以是用户体验也可以是生态),以至于其他参与者无法轻易获取这部分市场。比如微信,他的护城河是他的产品体验以及庞大的社交基础,后来者很难轻易的再造一个微信从他这里夺走用户。而这个能力就是所谓的护城河。

在打造一个产品的时候,很多时间也需要考虑护城河的问题。因为 UI 界面,交互这些都是可以被轻易复制的东西,而打造模式才是将竞争对手甩在身后的有利武器。我也做过没有护城河的产品,因此深深知道,类似的产品一旦被人模仿,利润就会被打得很薄,而不能额外获取更多的利润。

我在 W2SOLO 上也看到过某开发者投诉被碰瓷的事情(像素级抄袭),虽然很同情他,但这是一般工具类产品都会遇到的问题。一般工具类产品都是为了解决某个特定的小问题,非衣食住行等消费类每天都会接触,一般比较低频,这也导致了品牌建立困难,用户留存度低,从赛道来讲是先天不足,第二、工具类产品技术壁垒也很低,一般由个人开发者开发维护,项目逻辑复杂度低,技术难度小,容易复制。

继续阅读产品护城河杂感

独立开发者宣言

宣言两字用的比较大,但是很符合独立开发者渺小又要发声的定位。本文节选自 W2SOLO chicken(肯德鸡)自我介绍

全职开发者和独立开发者工作性质不同。全职 APP 开发者处理的是公司的业务中的一部分,面对的是产品经理、测试人员、美工设计、后端工程师等。按组织的项目计划和要求尽好自己的职责完成工作,月底(到)公司收工资。

(而)独立开发者的工作更像一种创业活动,为了容易接触广大消费者和其他平台公司合作,需要注册公司,商标注册,亲自了解市场客户需求,产品设计、产品开发测试迭代、产品推广运营等工作,和创业一样需要承担经营风险,(以防)竞争对手模仿,自负盈亏(等)。 独立开发者工作是一个成长过程也是一种对自己的投资。

我觉得每个 APP 的核心价值和用户群体需要沉淀,也不是每个开发者运气那么好都像买股票一样能追上风口,但怀着帮助人们更好地生活,解决问题烦恼的心,加深对用户的沟通了解,一定会找到自己的客户贡献。