duangsuse-valid-projects / GeekApk

GeekApk, the dying SpringBoot(a.k.a. Sping initializr) server for GeekApk(a.k.a 极安) (R
https://geekapk.moe/
GNU Affero General Public License v3.0
7 stars 2 forks source link

[GA v1♭] [NOTES] TODO() by duangsuse #11

Open duangsuse opened 5 years ago

duangsuse commented 5 years ago

TODO

https://github.com/bjzhou/Coolapk-kotlin

TODO later

"NOT" TODO

WHY: GeekApk Users can create apps on their purpose, and GeekApk have a GeekWidget plugin replacing coolapk's App discovery (inline picture in comments)

duangsuse commented 5 years ago

cup

duangsuse commented 5 years ago

目前 GeekApk 的控制器路径模板已经在 add-controller-template 分支上开发

填充完所有模板并合并到 master 后,下一个目标 branch 是 add-controller-tests

其时 v1b 的第一个版本所有非控制器的编程需求都没有了(因为之前都写完了,感谢 SpringBoot 使得我不用写那么多难看并且耗时间的 XML 配置文件),接下来就是测试驱动编程 implement-interfaces 喽

Test-driven development 的一个好处就是出来的肯定是能用的成品,因为它通过了验收测试(Acceptance test)

然后就是写前端

duangsuse commented 5 years ago

[DuangSUSE@duangsuse]~/文档/geekapk_spring% node code_writer.js

serverDetail() -> object:detailMap = /serverDetail

serverVersion() -> plain = /serverVersion

serverDescription() -> plain = /serverDescription

serverBoot() -> datetime = /serverBoot

POST@createUser(username:String) -> object:GeekUser = /admin/makeUser

PUT@resetSharedHash(uid-path:UserId, shash:String?) -> plain = /admin/resetMetaHash/{uid}

DELETE@deleteUser(uid-path:UserId) -> object:GeekUser = /admin/dropUser/{uid}

PUT@flagUser(uid-path:UserId, flag:Int) -> object:GeekUser = /admin/flagUser/{uid}

POST@createCategory(name:String) -> object:Category = /admin/makeCategory

PUT@renameCategory(id-path:CategoryId, name:String) -> object:Category = /admin/nameCategory/{id}

DELETE@deleteCategory(id-path:CategoryId) -> object:Category = /admin/dropCategory/{id}

DELETE@deleteApp(aid-path:AppId) -> object:App = /admin/dropApp/{aid}

PUT@transferAppCategory(aid-path:AppId, cid:CategoryId) -> [$aid:number, $old:number, $new:number] = /admin/moveApp/{aid}

PUT@transferAppOwner(aid-path:AppId, uid:UserId) -> [$aid:number, $old:number, $new:number] = /admin/transferApp/{aid}

DELETE@deleteAppUpdate(aid-path:AppId, rev-path:Int) -> object:AppUpdate = /admin/dropAppUpdate/{aid}/{rev}

DELETE@deleteComment(cid-path:CommentId) -> [$cid:number, $deletedSubComments:number] = /admin/dropComment/{cid}

categoryList() -> array:Category = category/all

categoryName(id-path:CategoryId) -> plain = category/{id}

readUser(id-path:UserId) -> object:GeekUser = user/{id}

updateUser(id-path:UserId, prop:String, value-body:String) -> object:string = user/{id}

resetHash(id-path:UserId, shash:String, hash:String) -> object:string = user/{id}/hash

checkHash(id-path:UserId, hash:String) -> object:string = user/{id}/checkHash

listUser(sort:String?, sliceFrom:UserSize?, sliceTo:UserSize?) -> array:GeekUser = user/all

listMetaUser(sort:String?, sliceFrom:UserSize?, sliceTo:UserSize?) -> array:GeekUser = user/allHasMetaApp

searchUser(type:String?, kw-path:String, sort:String?) -> array:GeekUser = user/search/{kw}

updateOnlineTime(id-path:UserId) = user/{id}/online

[ { method: 'GET', name: 'serverDetail', args: [], return: { type: 'object', of: 'detailMap' }, url: '/serverDetail' }, { method: 'GET', name: 'serverVersion', args: [], return: 'plain', url: '/serverVersion' }, { method: 'GET', name: 'serverDescription', args: [], return: 'plain', url: '/serverDescription' }, { method: 'GET', name: 'serverBoot', args: [], return: 'datetime', url: '/serverBoot' }, { method: 'POST', name: 'createUser', args: [ [Object] ], return: { type: 'object', of: 'GeekUser' }, url: '/admin/makeUser' }, { method: 'PUT', name: 'resetSharedHash', args: [ [Object], [Object] ], return: 'plain', url: '/admin/resetMetaHash/{uid}' }, { method: 'DELETE', name: 'deleteUser', args: [ [Object] ], return: { type: 'object', of: 'GeekUser' }, url: '/admin/dropUser/{uid}' }, { method: 'PUT', name: 'flagUser', args: [ [Object], [Object] ], return: { type: 'object', of: 'GeekUser' }, url: '/admin/flagUser/{uid}' }, { method: 'POST', name: 'createCategory', args: [ [Object] ], return: { type: 'object', of: 'Category' }, url: '/admin/makeCategory' }, { method: 'PUT', name: 'renameCategory', args: [ [Object], [Object] ], return: { type: 'object', of: 'Category' }, url: '/admin/nameCategory/{id}' }, { method: 'DELETE', name: 'deleteCategory', args: [ [Object] ], return: { type: 'object', of: 'Category' }, url: '/admin/dropCategory/{id}' }, { method: 'DELETE', name: 'deleteApp', args: [ [Object] ], return: { type: 'object', of: 'App' }, url: '/admin/dropApp/{aid}' }, { method: 'PUT', name: 'transferAppCategory', args: [ [Object], [Object] ], return: [ [Object], [Object], [Object] ], url: '/admin/moveApp/{aid}' }, { method: 'PUT', name: 'transferAppOwner', args: [ [Object], [Object] ], return: [ [Object], [Object], [Object] ], url: '/admin/transferApp/{aid}' }, { method: 'DELETE', name: 'deleteAppUpdate', args: [ [Object], [Object] ], return: { type: 'object', of: 'AppUpdate' }, url: '/admin/dropAppUpdate/{aid}/{rev}' }, { method: 'DELETE', name: 'deleteComment', args: [ [Object] ], return: [ [Object], [Object] ], url: '/admin/dropComment/{cid}' }, { method: 'GET', name: 'categoryList', args: [], return: { type: 'array', of: 'Category' }, url: 'category/all' }, { method: 'GET', name: 'categoryName', args: [ [Object] ], return: 'plain', url: 'category/{id}' }, { method: 'GET', name: 'readUser', args: [ [Object] ], return: { type: 'object', of: 'GeekUser' }, url: 'user/{id}' }, { method: 'GET', name: 'updateUser', args: [ [Object], [Object], [Object] ], return: { type: 'object', of: 'string' }, url: 'user/{id}' }, { method: 'GET', name: 'resetHash', args: [ [Object], [Object], [Object] ], return: { type: 'object', of: 'string' }, url: 'user/{id}/hash' }, { method: 'GET', name: 'checkHash', args: [ [Object], [Object] ], return: { type: 'object', of: 'string' }, url: 'user/{id}/checkHash' }, { method: 'GET', name: 'listUser', args: [ [Object], [Object], [Object] ], return: { type: 'array', of: 'GeekUser' }, url: 'user/all' }, { method: 'GET', name: 'listMetaUser', args: [ [Object], [Object], [Object] ], return: { type: 'array', of: 'GeekUser' }, url: 'user/allHasMetaApp' }, { method: 'GET', name: 'searchUser', args: [ [Object], [Object], [Object] ], return: { type: 'array', of: 'GeekUser' }, url: 'user/search/{kw}' }, { method: 'GET', name: 'updateOnlineTime', args: [ [Object] ], return: null, url: 'user/{id}/online' } ]

GET /serverDetail: serverDetail() Returning object of detailMap

GET /serverVersion: serverVersion() Returning plain

GET /serverDescription: serverDescription() Returning plain

GET /serverBoot: serverBoot() Returning datetime

POST /admin/makeUser: createUser(username:String!) Returning object of GeekUser

PUT /admin/resetMetaHash/{uid}: resetSharedHash(uid-path:UserId!,shash:String?) Returning plain

DELETE /admin/dropUser/{uid}: deleteUser(uid-path:UserId!) Returning object of GeekUser

PUT /admin/flagUser/{uid}: flagUser(uid-path:UserId!,flag:Int!) Returning object of GeekUser

POST /admin/makeCategory: createCategory(name:String!) Returning object of Category

PUT /admin/nameCategory/{id}: renameCategory(id-path:CategoryId!,name:String!) Returning object of Category

DELETE /admin/dropCategory/{id}: deleteCategory(id-path:CategoryId!) Returning object of Category

DELETE /admin/dropApp/{aid}: deleteApp(aid-path:AppId!) Returning object of App

PUT /admin/moveApp/{aid}: transferAppCategory(aid-path:AppId!,cid:CategoryId!) Returning number:aid,number:old,number:new

PUT /admin/transferApp/{aid}: transferAppOwner(aid-path:AppId!,uid:UserId!) Returning number:aid,number:old,number:new

DELETE /admin/dropAppUpdate/{aid}/{rev}: deleteAppUpdate(aid-path:AppId!,rev-path:Int!) Returning object of AppUpdate

DELETE /admin/dropComment/{cid}: deleteComment(cid-path:CommentId!) Returning number:cid,number:deletedSubComments

GET category/all: categoryList() Returning array of Category

GET category/{id}: categoryName(id-path:CategoryId!) Returning plain

GET user/{id}: readUser(id-path:UserId!) Returning object of GeekUser

GET user/{id}: updateUser(id-path:UserId!,prop:String!,value-body:String!) Returning object of string

GET user/{id}/hash: resetHash(id-path:UserId!,shash:String!,hash:String!) Returning object of string

GET user/{id}/checkHash: checkHash(id-path:UserId!,hash:String!) Returning object of string

GET user/all: listUser(sort:String?,sliceFrom:UserSize?,sliceTo:UserSize?) Returning array of GeekUser

GET user/allHasMetaApp: listMetaUser(sort:String?,sliceFrom:UserSize?,sliceTo:UserSize?) Returning array of GeekUser

GET user/search/{kw}: searchUser(type:String?,kw-path:String!,sort:String?) Returning array of GeekUser

GET user/{id}/online: updateOnlineTime(id-path:UserId!) Returning noting [DuangSUSE@duangsuse]~/文档/geekapk_spring%

duangsuse commented 5 years ago

找到了个很有意思的名词:Dao(Data Access Object) 是这样的么?

duangsuse commented 5 years ago

screenshot_20190205_232310 Branch "add-tests" excluded per configuration.

duangsuse commented 5 years ago

screenshot_20190205_232030 贡献者 duangsuse 已经启用很无聊的 Commit GnuPG 签名,除了让 GitHub 开心外没有什么卵用。

duangsuse commented 5 years ago

screenshot_20190205_211709 百 Commit 纪念

duangsuse commented 5 years ago

Travis GitHub App Installed :wrench:

duangsuse commented 5 years ago

[In reply to duangsuse::Echo] git diff --stat fb0242a475e84fe12911840de5d7740b8fd075e0 abb5d6c47cb011c5059e8e27c4e569e87b58b50b

11 files changed, 343 insertions(+), 34 deletions(-)

在半个小时里,GeekSpec 自动生成了 300 多行模板代码,换算成人就是十分钟写 100 行啊,都是这种代码:

  @PutMapping("/{aid}")
  @ResponseBody
  fun updateApp(@PathVariable("aid") aid: AppId, @RequestParam("attr") attr: String/* Maybe package or icon or name or screenshots or readme */,
                @RequestBody value: String): Map<String, String> /* attr: String *//* oldVal: String */ {
    TODO()
  }

不无聊么? 上面的代码,为什么不可以这么写呢?

PUT@updateApp(aid-path:AppId, attr:String{package, icon, name, screenshots, readme}, val-body:String)
  -> [$attr:String, $oldVal:String]
= /app/{aid}

于是我居然花了一天时间弄了个 GeekApk 自己替换 Swagger 的,虽然没啥复杂算法... 但不得不说,真的很有用。 『磨刀不误砍柴工』

... CS 看多了又想到偏序理论... 头疼

duangsuse commented 5 years ago

screenshot_20190206_000441 🤔 GeekSpec 对 GeekApk Spring 的工程有多大的辅助作用呢?自 Spec 文件写完 https://github.com/duangsuse/GeekApk/commit/fb0242a475e84fe12911840de5d7740b8fd075e0 之后,自动生成解决了 300 多行模板代码的编写任务,从写完 7:34 到完成任务合并主分支 8:08 只花费了半个小时左右,可谓是一瞬生成了(

duangsuse commented 5 years ago

哇,除了 DAO,我又认识了一大堆那么专业的术语名词,听起来真是好大佬呢,好厉害呢!不明觉厉呢!哇!原来这就是 IT 啊,我要打十个!

MVC、MVP、MVVP、Databinding、ViewHolder、AOP、Provider、Resolver、Adaptor、Pattern、List 视图、PAJAX、AJAX、Reverse AJAX、Bean、CDI、Transactional、ACID、Functional、GC、Memory Leak、static field、obfuscation、anti-reversing、Machine learning、解耦合、Component、POJO、DSL、Parametric Types、Encryption、Cipher、Observer、Stream、Future、Dependencies、Box Model、Dispatcher、Handler、Scheduler、OOP、FP、Xml

SICP、TaPL、FoPL、CSAPP、SASP、…

你们说我是不是可以去写十个酷安了?(笑)

duangsuse commented 5 years ago

懒得用,因为我觉得不好看、不是。因为酷安的 API 客户端验证就用到了此类、DDG 上一搜全都是,并且我也看过 Oracle 的文档,我不可能看不到。实际上我妄图写了点 Kotlin/multiplatform 代码都用到了此类。

PostgreSQL 更成熟,而且我们的建模是关系型的,目前没有 lower 化模型的打算 芒果数据库(不准喷翻译)是文档型数据库,其实它这个接口很适合熟悉 Java collections API 的程序员使用。我们说关系的本质是图,但是图在电子计算机上往往使用 Map 和 (对于每个节点之间关系箭头的建立)Set 来构建。MongoDB 提供了易用的 API。 GeekApk 的模型很大,我很难一次做好(是的,即使我出过很多版本的文档) 不像隔壁,它的模型就比较简单(是的,而且他们用的 service 层又是别的… 我们为了速度先用的是手里有包含实际例子的 SpringBoot。 LBA 架构么?总是说术语… 无聊,而且这就像拼音和五笔一样,明明都差不多,各有所好因为各种有不同的经历)

要喷这个,请大人您先考虑一下 GeekApk v1b 的模型,和上面那个比起来哪个大。注意我们(GA Spring 和 Rebase-server)的权限验证都没有用外部的 CDI 库。

  1. 我把自己当少爷,懒得管那些低层机械化的任务和模板代码,让 Spring 帮我弄不是提高效率么(再说一定要写现在资料也不难查,我也有 Internet 的教科书
  2. 我要卖小、我才 17 岁,隔壁那个 13 年入 gh 的老油条了,而且 when 隔壁已经发布能用的第一版 0.9,我 17 年 1 月才会写很模式化的 Java,而且我看不透后面那些东西,非常幼稚 naïve 并且啥算法不会写。我写这个的时候刚刚接触软件工程两年。拿一个至少 4 年经验而且专攻 JVM 工程的老油条你们良心上过得去么?我还只是个孩子所以请不要拿资深全栈吊打我 小心我成资深后端了回来各种吊打你们
  3. 再不说,本项目刚开 7 天,不算我没写代码(比如走亲戚和修整)的总共才开 4 天,隔壁那个从 Hello world 到第一个 release 0.9 就从 Jan 14..Jan 21 已经有至少(减去休息一天 6 天时间),老项目了。(迫真)
  4. 我这个开源的,说明我不把它当回事,隔壁那个源代码删了,说明隔壁很觉得此作怎么样(注意诡辩)
  5. 瘦死的骆驼比马大啊… 这些基本看完就会整个源代码里没几个类引用的东西,比起函数式编程、信号处理、编译原理的一些书论文文章,哪个更困难?哪个更需要脑子而不是死记?

不说那些基本就是非常简单的 proxy (和设计模式无关) extract 的东西(比如那些单纯包装外部 procedure 的类、静态方法、静态字段)了,咱看看这个:

@Constraint(validatedBy = { CategoryKey.Validator.class })
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
public @interface CategoryKey {

    int min() default 1;

    int max() default Globals.SIZE_CATEGORY;

    String message() default "{org.hibernate.validator.constraints.Length.message}";
// Validator implementation omitted

80% 的模板代码,而且三个文件中可以说这些代码都是重复的。我看一遍这些模板代码使用的接口也就大概了解了,请问这算什么?不算 Java 8 语法的部分,我只需要知道 @Constraint(validatedBy: Class<?>[]) min max message org.hibernate.validator.constraints.Length.message Class<?>[] groups() Class<? extends Payload>[] payload() Validator implements ConstraintValidator<CategoryKey, String> { initialize (annotation); boolean validate/*update: isValid*/(value, context); },就算学会了这知识。何况 IDEA 现在那么方便,应该比谁更会用特性和快捷键(e.g. Ctrl N)才是。

我看完这些就是满脑子名词,Resources、Provider… XXXXs、YYYYMapper… 然而,比起理解递归、解决各种动态规划甚至线性代数问题,这些接口了解性质的东西真的可谓是『了解』而不能算作是学习… 这点概念哪里配得上『学习』这个词… 配得上机器学习里的学习这个词

我很心虚,觉得自己的代码不好,就去看别人的取长补短。我是菜🐔别人是大佬不行吗?

duangsuse commented 5 years ago
* 那么多人用 J2SE 的 UUID 类,你为什么不用,非得自己写?是不是你连 JSE 有这个都不知道?

....

行了... 好无聊啊,其实要看的话,要比较的话也没有可比性,那个设计模式更多一些(),但它不能证明什么,因为这些代码也可以抄,目前本项目的代码都是我抄书上抄来的。

我目前的任务就是弄完 GeekApk,其他的再说。

duangsuse 有时候会有点神经质,因为我对技术看得太重... 如有不便还请大家谅解。刚才想要不要彻底分析一下刚才那个项目,顺便熟悉一下工程的说(因为我平时工程做得比较少,可以作为一种提升,虽然也模式化但比这个 Spring 的稍微好一点),可是因为 GA 最好还是先弄完(包括前端)就算了。

:pray: :boy:

duangsuse commented 5 years ago

https://github.com/Codingpedia/demo-rest-jersey-spring

emmmm DataAccessObject 和 Entity 居然不是一样的 🤔 #Backend #web

不过我现在几乎没有看到 DAO、大概是后端的书不够多...

duangsuse commented 5 years ago

https://github.com/imufun/rebase-server/tree/master/src/main/java/com/drakeet/rebase/api/tool

我已经正常分析了此项目并且理解了所有 Extracts(Authorizations, Globals, Hashes, Log, MongoDBs, RebaseAsserts, Responses, URIs)、Mapper(ConstraintViolationExceptionMapper, WebExceptionMapper)、Provider(GsonBodyProvider)、Resolver(ValidationConfigurationContextResolver)、Implementation(ObjectIdSerializer)... 还有他们自己的用途、模式、运行时的数据流和控制流,并和我之前的知识相结合,并且在正常的时间内完成,不会崩坏了。thx。GeekApk 会照常以自己的风格完成开发。

duangsuse commented 5 years ago

since 目前我写了第一个含写的接口业务逻辑测试,结果写了半天 200 多行代码... 而且很多代码... 呃... 很难看,因为 JUnit 的原因,貌似没办法保持一定顺序执行(因为我这个是要求某些操作顺序的,之间互相有依赖关系),而且这个测试质量很差... 而且单独去给所有接口写测试是非常淡疼的。我决定写保护 add-tests 分支,跳过测试编写,直接开始编写业务逻辑。而且我希望尽快完成任务,,, 所有 v1b 的 Boot Server 可能不会弄 test-driven development,emmm....

https://github.com/duangsuse/GeekApk/commit/58a32fbc127df3cbd4b6c30e624c88231b58bb47 割肉删掉了测试.. 以后自动化测试会由 Ruby 执行

duangsuse commented 5 years ago

♥\_(ㇱ)_/✐ :point_left: 疯狂暗示用爱写代码 hhhh :rofl:

duangsuse commented 5 years ago

Ruby 的 Spectrum 打算先暂时 JSON,因为我最近有点... 不想写 Ruby? 其实是嫌浪费时间了

duangsuse commented 5 years ago

[ Photo ] 累死,算是学习(确信)了一下 Ruby 和测试了 Spectrum... 感觉解耦合做得不是很好,response mapper 根本一点用都没有(因为要很多 mapper 必须要拿到一个 ClientShowcase 实例),另外给 ClientShowcase 添加一个 Hash 表来放诸如密码这样的东西隐式传递不错),考虑重构。

duangsuse commented 5 years ago

[In reply to duangsuse::Echo] Times 是之前老 GeekApkR 里唯一一个能用的后端服务... Ruby + Sinatra 写的,其实现在看来都很不良实践(跑) 凑字数(代码行数)吧(

不过它的测试套件也算完整,虽然持久化层的不是 Mock,但也可以用,缺点是如果在生产环境用了很可能导致数据丢失

Times 没有使用 DBMS,而是,这是因为我以为它不会有太大的数据集性能要求,我觉得很可以重构(比方说,使用类似 MongoDB 的面向文档数据库替换纯手动)... 尤其是难看的 API...

值得注意的是我以前智商还低一些的时候,连常用的序列化这种弱智能力都没有... JSON 序列化(Times 的数据持久化方式)当时非常的混乱,我非常智障的套模板(或许,因为我想我那时就是瞎搅合...)

我没有么写:

[foo, bar, baz].to_json

而是:

[foo, bar, baz].map { |o| o.to_json }.to_json

然后反序列也一样... rofl.

duangsuse commented 5 years ago

[In reply to duangsuse::Echo] 回来了(因为身体素质真的开始有点 🌶🐔 了,而且非常需要休息,然后老是熬夜容易变丑,虽然其实长的就不咋地,大嘘),顺便添了一些任务

  1. 部署文档添加 Systemd 低端口权限配置提示(服务程序部署安全,这样如果我们的 upstream 有严重的,例如 RCE、可以拿 Shell 的安全问题别人不至于一下子迫真 rm -rf / 殃及池鱼) :heavy_check_mark:

  2. 把所有 Endpoint mapping 的 Int? 换成 java.lang.Integer? (要不然错误,说 int 不能是 optional 的,虽然本来有默认值 0)(这个打脸了,我真的不是不了解 Kotlin... 我真的只是被 Spring 的提示误导了... emmm) :heavy_check_mark:

  3. 为了 Clients 数据交换开心,添加 PATCH Json API(现在创建用户之后可以直接拿 JSON 去 patch 属性了,不用一个一个属性去 update) :heavy_check_mark: (现在还不是 RESTful 的)

  4. 添加用户和管理员权限验证的 Middleware(WebFilter)、其中用户的权限验证和 Rate Limit 一起做(GeekHits API 也一起开放了)

  5. 为 CI Intergation 添加 Node.JS 和 MRI Ruby 依赖,并且开始使用外围的 Spectrum Ruby 客户端接口测试(从 JUnit 启动服务器和 RSpec 客户端自动化测试程序) :heavy_check_mark:

GeekApk v1b 完成后可能有别的打算(包括 Android 客户端,不准喷我不会什么设计模式)(有些人不准喷我菜...)(虽然我有时候的确很菜,但我自己觉得我很有前途,嗯嗯。)

希望以后都是艳阳高照。冻死了天又黑...

duangsuse commented 5 years ago

下一步操作是 WebHooks(GeekPush)支持、Topics 等和 MessageHeader 的支持、包括工具的其他平台移植

duangsuse commented 5 years ago

== 晚点

== 发布之后