duangsues.is_a? SaltedFish
59 subscribers
609 photos
6 videos
91 files
562 links
🌶🐔🐟 duangsuse 的日常
尤其喜欢发些奇奇怪怪的东西
和转载别人的东西
Download Telegram
id | SMALLSERIAL | | PRIMARY KEY
simple_name | VARCHAR | 非空、长度小于 20 个字符、只能包含 [A-z]`、[a-z][0-9]、`_ | 用户的机器可读名称
avatar_url | VARCHAR | 长度小于 500 字符 | 用户的头像 _URL_
user_name | VARCHAR | 非空、长度小于 100 字符 | 用户名
alias | VARCHAR | 长度小于 50 字符 | 用户别名
bio | VARCHAR | 非空、长度小于 500 字符 | DEFAULT '' 用户自我介绍
dev_bio | VARCHAR | 长度小于 500 字符 | 作为开发者的自我介绍
created_at | TIMESTAMP | 非空 | DEFAULT now() 用户创建时间
online_at | TIMESTAMP | 非空 | DEFAULT now() 用户最近上线时间,由用户手动更新
followers_num | SMALLINT | 非空 | DEFAULT 0 用户的跟随者数目
enabled | BOOLEAN | 非空 | DEFAULT true 用户是否启用
id | SMALLSERIAL | | PRIMARY KEY
category_name | VARCHAR | 非空 | 分类名,因为只有内部人员操作不加限制
应用的 ID 现在就是其包名,一个字符串
id | VARCHAR | | PRIMARY KEY
author_user | SMALLINT | 非空 | 创建者 _UID_
category | SMALLINT | 非空 | 归属 _TID_
package_name | VARCHAR | 长度小于 60 字符 | 包名
app_name | VARCHAR | 非空、长度小于 60 字符 | 应用名
alias | VARCHAR | 长度小于 60 字符 | 别名
icon_url | VARCHAR | 长度小于 500 字符 | 图标 _URL_
app_description | VARCHAR | 非空、长度小于 10000 字符 | DEFAULT '' 应用描述
visualizer | VARCHAR | 长度小于 20 字符 | 应用模型视图
button_text | VARCHAR | 长度小于 60 字符 | _安装卸载按钮_ 文本覆盖
special | VARCHAR | 长度小于 12 字符 | 特殊标识
previews | VARCHAR | 长度小于 4000 字符 | 以 ';' 切分的预览图 _URL_
app_permissions | VARCHAR | 长度小于 10000 字符 | 以 '\n' 切分的权限列表
size | INTEGER | 非空 | DEFAULT 0 应用安装包以 百字节 计体积
created_at | TIMESTAMP | 非空 | DEFAULT now() 创建时间
updated_at | TIMESTAMP | 非空 | DEFAULT now() 更新时间
stars_num | SMALLINT | 非空 | DEFAULT 0 星标数
comments_num | INTEGER | 非空 | DEFAULT 0 评论数
id | SERIAL | | PRIMARY KEY
author_user | SMALLINT | 非空 | 创建人 _UID_
app | SMALLINT | 非空 | 评论 _AID_
reply_comment | INTEGER | | 回复 _CID_
content | VARCHAR | 非空、长度小于 7000 字符 | 评论内容

replies_num | INTEGER | 非空 | DEFAULT 0 回复数
created_at | TIMESTAMP | 非空 | DEFAULT now() 创建时间
updated_at | TIMESTAMP | | 修改时间
user | SMALLINT | 非空 | 跟随者 _UID_
followed_user | SMALLINT | 非空 | 被跟随者 _UID_
user | SMALLINT | | PRIMARY KEY 目标用户
metapass | VARCHAR | 非空 | 分发密码
passhash | VARCHAR | | SHA-256 密码取样
app | SMALLINT | 非空 | 目标 _AID_
version_name | VARCHAR | 非空,长度小于 40 字符 | 版本名
reversion | SMALLINT | 非空 | 修订号
install_url | VARCHAR | 非空,长度小于 500 字符 | 安装 _GFC_
updates | VARCHAR | 非空,长度小于 6000 字符 | 更新内容
api_min | SMALLINT | | 最低 SDK 版本
api_target | SMALLINT | | 最高 SDK 版本
user | SMALLINT | 非空 | 操作 _用户_
app | SMALLINT | 非空 | 目标 _应用_
user | SMALLINT | 非空 | _用户_
created_at | TIMESTAMP | 非空 | DEFAULT now() 创建时间
line_type | SMALLINT | 非空 | 时间线类型
line_data | INTEGER | 非空 | 时间线数据
user | SMALLINT | 非空 | _用户_
created_at | TIMESTAMP | 非空 | DEFAULT now() 创建时间
notification_type | SMALLINT | 非空 | 通知类型
notification_data | INTEGER | 非空 | 通知数据
enabled | BOOLEAN | 非空 | DEFAULT false 已经阅读
通知

GeekApk 里,通知 作为一种对 用户 的提醒而存在,一般在 动作执行后 直接保存等待用户阅读,GeekApk 中有这些通知:

用户 的 评论 被 回复 的通知
用户 被 @ 提到 的通知
用户 被 用户 跟随 的通知

Timeline(时间线)

类似 GitHub,GeekApk 也会记录每个 用户 的公开活动并允许所有人自由查询,这个特性被称为 Timeline(时间线),GeekApk 中有这些时间线记录:

用户 Follow 了某个 用户
用户 创建了 某个 评论
用户 更新了 某个 评论
用户 发布了 某个 应用
用户 发布了 某个 应用 的 更新
用户 删除了 某个 应用
用户 Star 了某个 应用

排序

用户可按照跟随数目排序,这要在数据库表里增加 followers_num 字段

应用按照 Star 数、Comment 数、更新时间排序,这要在表里增加 stars_numcomments_num 字段

评论只能按照创建时间排序

权限

权限还是用户可写,全服只有一个管理帐号使用 DOGE_TOK 验证,只行使管理员权限而没有用户权限
用户还是 uidhash 两个 cookie、分发密码称为 metahash.

计算密码的 Hash 值过程由客户端执行,所有官方客户端都自动取 Hash 值作为密码

简易模型

用户 | user | users | uid | GeekApk 用户帐号

用户可以跟随其他用户,用户有 metahash 和 hash,用户可以发布评论/应用/更新/星标

分类 | category | categories | tid | GeekApk 应用分类

管理员可以创建/删除分类、修改分类名

应用 | app | apps | aid | GeekApk 应用

应用可以被附加更新和评论,被用户星标

评论 | comment | comments | cid | GeekApk 评论

评论可以被附加子评论

具体

用户有简易名称、用户名和不可修改的 UID、头像链接、介绍、创建时间、小黑屋状态
分类有自己不可修改的 ID 和名称,一般以 / 切分路径
应用有不可修改的 ID(包名)、创建用户的 ID 引用、所属分类的 ID 引用、名称、图标链接、应用描述、预览图像、创建时间和更新时间
更新有所属应用的 ID 引用、版本名、reversion、安装链接、更新内容、最低和最高适配 SDK 版本
评论有不可修改的 ID、创建用户的 ID 引用、所属应用的 ID 引用、回复评论的 ID 引用、内容、创建时间和更新时间、被回复数

时间线有所属用户引用、创建时间、类型、数据引用
通知有所属用户、创建时间、类型、数据引用、已读状态

为了允许在用户和应用间创建 Star 关系、用户和用户之前创建跟随关系,还额外建立了 followstar
傲梦 OJ 相关的 API(貌似是自己写的,要不然不会连 Vue.js 开发模式都没关(这次我再找到有啥问题不会发邮件通知他们了,发了也没好脸色看)👎

GET https://all-dream.com/napi/cfxy/questions

获得试题组,返回一个 JSON 列表,每个试题看起来是这样的:

  {
"id": 16,
"name": "造梯子",
"description": "见惯了生活中的木梯子、石梯子,你见过用*造的梯子吗?那么现在尝试去造一个*做的梯子吧!\r\n\r\n注:每两个相邻的\"*\"之间用一个空格隔开,且每行没有多余的空格。",
"chapter_id": "0636f9bc-aff3-4bed-b38d-ab1242d4a88d",
"reward": 1,
"create_time": "2016-04-29T11:05:01.000Z",
"intput": "无",
"output": "*\r\n**\r\n***",
"example_input": "无",
"example_output": "*\r\n**\r\n***",
"tips": "printf结合\\n的使用",
"attempt": 511,
"pass": 420,
"time_limit": 1000,
"test_time_limit": 1000,
"memory_limit": 50,
"in_file_path": "",
"out_file_path": "ONLINETEST/6da99376-3237-4b5a-b991-e6e1b8e08382/1.out,",
"sort_num": 1,
"oss": "6da99376-3237-4b5a-b991-e6e1b8e08382"
}

GET https://all-dream.com/napi/cfxy/chapters

获得小类(题类)组,大类下面是小类,小类下面是题目,返回数组

  {
"id": "0636f9bc-aff3-4bed-b38d-ab1242d4a88d",
"name": "C1. 输出",
"group_id": "7fbcb3aa-da34-455e-ac75-6fcee72ed2f3",
"description": "输出",
"create_time": "2016-04-29T10:31:55.000Z",
"attempt": 557,
"pass": 78,
"sort_num": 1
}

GET https://all-dream.com/napi/cfxy/group

获得大类列表,每个大类对象看起来像这样

  {
"id": "7fbcb3aa-da34-455e-ac75-6fcee72ed2f3",
"name": "C语言",
"description": "",
"sort_num": 1
}

获得运行记录

GET/POST https://all-dream.com/napi/cfxy/runlog?userId=uid

返回 uid 的运行记录题目 ID JSON 列表

尝试运行代码

POST https://all-dream.com/napi/cfxy/runcode
参数 code、questionId、userId

响应 JSON 包含 id(qid)、code、ret JSON({"info": [{"cpu_time": 1, "exit_status": 0, "signal": 0, "real_time": 2, "flag": 0, "result": 6, "memory": 22884352, "output": "", "message": "\u5b9e\u9645\u8f93\u51fa\u548c\u671f\u671b\u8f93\u51fa\u4e0d\u4e00\u81f4"}], "result": 6})
JavaScript 版本

function runlog(uid, fun) {
var req = new XMLHttpRequest();
req.addEventListener('load', fun);
req.open('POST', 'https://all-dream.com/napi/cfxy/runlog');
req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
req.send('userId=' + uid);
return req;
}
function runlog(uid, fun) {
var request = require("request");
return request({
uri: "https://all-dream.com/napi/cfxy/runlog",
method: "POST",
form: {
userId: uid
}
}, function(error, response, body) {
fun(JSON.parse(body));
});
}

runlog('d21ad8f718264854a4dee4dc4a11badd', console.log)
function runcode(uid, qid, code, fun) {
var request = require("request");
return request({
uri: "https://all-dream.com/napi/cfxy/runcode",
method: "POST",
form: {
userId: uid, questionId: qid, code: code
}
}, function(error, response, body) {
fun(JSON.parse(body));
});
}
runcode('d21ad8f718264854a4dee4dc4a11badd', 18, '', console.log)
function runlogPromise(uid) {
var request = require("request");
return new Promise((resolve, reject) => {
request({ uri: 'https://all-dream.com/napi/cfxy/runlog', method: 'POST', form: { userId: uid } },
(err, resp, body) => {
if (err) { reject(err) } else { resolve(JSON.parse(body)); }
});
});
}

runlogPromise('d21ad8f718264854a4dee4dc4a11badd').then(console.log)