Commit ec9aa6f1 authored by Chunchi Che's avatar Chunchi Che

Merge branch 'feat/service/cards' into 'main'

Feat/service/cards

See merge request !2
parents 2e57c936 d1b80598
Pipeline #18105 failed with stages
in 3 minutes and 47 seconds
...@@ -13,3 +13,6 @@ Cargo.lock ...@@ -13,3 +13,6 @@ Cargo.lock
# Added by cargo # Added by cargo
/target /target
# gitlab-runner product
/builds
...@@ -4,6 +4,15 @@ stages: ...@@ -4,6 +4,15 @@ stages:
- build - build
- deploy - deploy
before_script:
- 'command -v ssh-agent >/dev/null || ( apt-get update -y && apt-get install openssh-client -y )'
- eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
- git config --global user.email "linuxgnulover@gmail.com"
- git config --global user.name "SKTT1Ryze"
cargo_check: cargo_check:
stage: lint stage: lint
image: rust:latest image: rust:latest
...@@ -27,6 +36,7 @@ cargo_test: ...@@ -27,6 +36,7 @@ cargo_test:
tags: tags:
- linux - linux
script: script:
- git submodule update --init --recursive
- cargo test - cargo test
cargo_build: cargo_build:
......
...@@ -14,3 +14,4 @@ pretty_env_logger = "0.4" ...@@ -14,3 +14,4 @@ pretty_env_logger = "0.4"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1" serde_json = "1"
diesel = { version = "2.0.0", features = ["sqlite"] } diesel = { version = "2.0.0", features = ["sqlite"] }
diesel_derives = { version = "2.0.0", features = ["sqlite"] }
use std::ops::{Deref, DerefMut};
use diesel::{Connection, SqliteConnection}; use diesel::{Connection, SqliteConnection};
pub enum DbConn { pub enum DbConn {
...@@ -11,6 +13,24 @@ impl DbConn { ...@@ -11,6 +13,24 @@ impl DbConn {
} }
} }
impl Deref for DbConn {
type Target = SqliteConnection;
fn deref(&self) -> &Self::Target {
match self {
DbConn::SqliteConn(conn) => conn,
}
}
}
impl DerefMut for DbConn {
fn deref_mut(&mut self) -> &mut Self::Target {
match self {
DbConn::SqliteConn(conn) => conn,
}
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::DbConn; use super::DbConn;
...@@ -18,7 +38,7 @@ mod tests { ...@@ -18,7 +38,7 @@ mod tests {
#[test] #[test]
fn test_establish_sqlite() { fn test_establish_sqlite() {
let workspace = env!("CARGO_MANIFEST_DIR"); let workspace = env!("CARGO_MANIFEST_DIR");
let db_url = format!("{}/ygopro-database/locales/zh-CN/cards.db", workspace); let db_url = format!("{}/ygopro-database/locales/zh-CN/cards.cdb", workspace);
let _conn = DbConn::establish_sqlite(&db_url).unwrap(); let _conn = DbConn::establish_sqlite(&db_url).unwrap();
} }
} }
mod model;
mod schema;
use crate::infra::DbConn;
use diesel::{ExpressionMethods, QueryDsl, RunQueryDsl};
use std::collections::HashMap;
pub use model::{CardDatas, CardTexts};
/// `cards.cdb`业务层模块
pub struct CardsDB {
conn: DbConn,
}
impl CardsDB {
/// 创建`CardsDB`实例
///
/// TODO: 这里更好得做法应该是由基建层维护db连接池,
/// 业务方通过取到db连接来做业务逻辑
pub fn new() -> anyhow::Result<Self> {
let workspace = env!("CARGO_MANIFEST_DIR");
let db_url = format!("{}/ygopro-database/locales/zh-CN/cards.cdb", workspace);
let conn = DbConn::establish_sqlite(&db_url)?;
Ok(Self { conn })
}
/// 通过`id`获取`Card`元数据
pub fn select_card_datas(&mut self, ids: &[i64]) -> anyhow::Result<HashMap<i64, CardDatas>> {
use schema::datas;
let conn = &mut *self.conn;
let records: Vec<CardDatas> = datas::table.filter(datas::id.eq_any(ids)).load(conn)?;
let records = records
.into_iter()
.map(|record| (record.id, record))
.collect();
Ok(records)
}
/// 通过`id`获取`Card`文本数据
pub fn select_card_texts(&mut self, ids: &[i64]) -> anyhow::Result<HashMap<i64, CardTexts>> {
use schema::texts;
let conn = &mut *self.conn;
let records: Vec<CardTexts> = texts::table.filter(texts::id.eq_any(ids)).load(conn)?;
let records = records
.into_iter()
.map(|record| (record.id, record))
.collect();
Ok(records)
}
}
#[cfg(test)]
mod tests {
use super::{
model::{CardDatas, CardTexts},
CardsDB,
};
#[test]
fn test_select_card_datas() {
let mut db = CardsDB::new().unwrap();
let id = 2333365;
let data = db.select_card_datas(&[id]).unwrap().remove(&id).unwrap();
assert_eq!(
data,
CardDatas {
id: 2333365,
ot: 3,
alias: 0,
setcode: 66,
type_: 33,
atk: 2000,
def: 2000,
level: 4,
race: 1,
attribute: 16,
category: 65536
}
)
}
#[test]
fn test_select_card_texts() {
let mut db = CardsDB::new().unwrap();
let id = 2333365;
let text = db.select_card_texts(&[id]).unwrap().remove(&id).unwrap();
assert_eq!(
text,
CardTexts {
id: 2333365,
name: "极星将 提尔".into(),
desc: Some(
"场上没有这张卡以外的名字带有「极星」的怪兽表侧表示存在的场合,这张卡破坏。\
只要这张卡在场上表侧表示存在,对方不能选择「极星将 \
提尔」以外的名字带有「极星」的怪兽作为攻击对象。"
.into()
),
str1: Some("".into()),
str2: Some("".into()),
str3: Some("".into()),
str4: Some("".into()),
str5: Some("".into()),
str6: Some("".into()),
str7: Some("".into()),
str8: Some("".into()),
str9: Some("".into()),
str10: Some("".into()),
str11: Some("".into()),
str12: Some("".into()),
str13: Some("".into()),
str14: Some("".into()),
str15: Some("".into()),
str16: Some("".into())
}
)
}
}
use diesel_derives::Queryable;
#[derive(serde::Serialize, Queryable, Clone, Debug, Default, PartialEq, Eq)]
#[diesel(table_name = datas)]
pub struct CardDatas {
pub id: i64,
pub ot: i32,
pub alias: i32,
pub setcode: i32,
pub type_: i32,
pub atk: i32,
pub def: i32,
pub level: i32,
pub race: i32,
pub attribute: i32,
pub category: i32,
}
// TODO: 这里字段应该命名得更清晰一点
#[derive(serde::Serialize, Queryable, Clone, Debug, Default, PartialEq, Eq)]
#[diesel(table_name = texts)]
pub struct CardTexts {
pub id: i64,
pub name: String,
pub desc: Option<String>,
pub str1: Option<String>,
pub str2: Option<String>,
pub str3: Option<String>,
pub str4: Option<String>,
pub str5: Option<String>,
pub str6: Option<String>,
pub str7: Option<String>,
pub str8: Option<String>,
pub str9: Option<String>,
pub str10: Option<String>,
pub str11: Option<String>,
pub str12: Option<String>,
pub str13: Option<String>,
pub str14: Option<String>,
pub str15: Option<String>,
pub str16: Option<String>,
}
diesel::table! {
datas (id) {
id -> BigInt,
ot -> Integer,
alias -> Integer,
setcode -> Integer,
#[sql_name = "type"]
type_ -> Integer,
atk -> Integer,
def -> Integer,
level -> Integer,
race -> Integer,
attribute -> Integer,
category -> Integer,
}
}
diesel::table! {
texts (id) {
id -> BigInt,
name -> Text,
desc -> Nullable<Text>,
str1 -> Nullable<Text>,
str2 -> Nullable<Text>,
str3 -> Nullable<Text>,
str4 -> Nullable<Text>,
str5 -> Nullable<Text>,
str6 -> Nullable<Text>,
str7 -> Nullable<Text>,
str8 -> Nullable<Text>,
str9 -> Nullable<Text>,
str10 -> Nullable<Text>,
str11 -> Nullable<Text>,
str12 -> Nullable<Text>,
str13 -> Nullable<Text>,
str14 -> Nullable<Text>,
str15 -> Nullable<Text>,
str16 -> Nullable<Text>,
}
}
mod db;
// TODO: 这里更合理的做法应该是前端通过`POST`请求
// 数据,二进制格式使用PB协议编码,PB文件定义在`neos-protobuf`项目里面,
// `neos-rs`项目添加`neos-protobuf`项目作为子模块。
//
// 暂时先实现简单的`GET`接口
pub fn service(param: String) -> String { pub fn service(param: String) -> String {
todo!() let id = param.parse::<i64>().unwrap_or_default();
match (|| {
// TODO: 应该在服务冷启动的时候获取`CardsDB`实例,
// 而不是在每次`HTTP`请求的时候重新获取
let mut cards_db = db::CardsDB::new()?;
let datas = cards_db
.select_card_datas(&[id])?
.remove(&id)
.unwrap_or_default();
let texts = cards_db
.select_card_texts(&[id])?
.remove(&id)
.unwrap_or_default();
Ok::<(db::CardDatas, db::CardTexts), anyhow::Error>((datas, texts))
})() {
Ok((data, text)) => {
let meta = CardMeta { data, text };
serde_json::to_string(&meta).unwrap_or_default()
}
Err(e) => {
log::error!("Handle cards service error={:?}", e);
serde_json::to_string(&CardMeta::default()).unwrap_or_default()
}
}
}
#[derive(serde::Serialize, Default, Debug)]
pub struct CardMeta {
pub data: db::CardDatas,
pub text: db::CardTexts,
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment