v1.1.0: Alpine 轻量级 Docker 开发环境 + 敏感凭证清理
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
# Flux 开发环境
|
||||
|
||||
> 当前在 ws-flux-dev 容器内
|
||||
> 工作目录: /workspace (= E:/wk-flux)
|
||||
|
||||
## 项目概述
|
||||
|
||||
贷款撮合平台 (Flux),核心模块:
|
||||
|
||||
| 模块 | 技术栈 | 说明 |
|
||||
|------|--------|------|
|
||||
| flux-api | Java 17 + Redkale + Maven | 后端 API,核心业务 |
|
||||
| flux-admin | Vue 3 + Arco Design + bun | 管理后台 |
|
||||
| flux-uniapp | UniApp + Vite | H5 前端 |
|
||||
| flux-mock | Go | 三方模拟服务 |
|
||||
| flux-test | Go | 业务链测试 (撞库等) |
|
||||
| file-server | Go | 文件上传下载 |
|
||||
| flux-wechat-api | — | 微信服务端 |
|
||||
|
||||
## 代理工具(优先使用)
|
||||
|
||||
> **容器内代理服务由 .bashrc 自动启动**,无需手动启动。
|
||||
> **所有数据库和远程操作必须通过代理工具执行**,禁止直接用 mysql/ssh/redis-cli。
|
||||
|
||||
### MySQL 查询
|
||||
|
||||
```bash
|
||||
# 查询 (默认表格输出)
|
||||
mysql-proxy cli -c flux_dev -e "SELECT * FROM users LIMIT 10"
|
||||
|
||||
# JSON 输出 (便于解析)
|
||||
mysql-proxy cli -c flux_dev -e "SHOW TABLES" -F json
|
||||
|
||||
# 执行 DML (加 -x)
|
||||
mysql-proxy cli -c flux_dev -e "UPDATE table SET status=1 WHERE id=1" -x
|
||||
```
|
||||
|
||||
### SSH 远程执行
|
||||
|
||||
```bash
|
||||
# 执行命令
|
||||
ssh-proxy exec -n flux_dev -c "docker ps"
|
||||
|
||||
# JSON 输出
|
||||
ssh-proxy exec -n flux_dev -c "docker ps" -F json
|
||||
|
||||
# 生产服务器 (byr_pro = prod_baiyarong)
|
||||
ssh-proxy exec -n byr_pro -c "cd /opt/flux-api && bash bin/restart.sh"
|
||||
```
|
||||
|
||||
### Redis 操作
|
||||
|
||||
```bash
|
||||
# 取值
|
||||
redis-proxy get -c flux_dev -k "session:user:123"
|
||||
|
||||
# 通用命令 (最灵活)
|
||||
redis-proxy run -c flux_dev -C "KEYS" -a "user:*"
|
||||
|
||||
# 设置值
|
||||
redis-proxy set -c flux_dev -k "key" -v "value"
|
||||
```
|
||||
|
||||
## 开发命令
|
||||
|
||||
```bash
|
||||
# ===== flux-api (Java/Redkale/Maven) =====
|
||||
cd /workspace/flux-api
|
||||
mvn clean package # 构建
|
||||
cmd /c bin\restart.bat # 重启 (Windows bat,容器内需调整)
|
||||
|
||||
# ===== flux-admin (Vue3/bun) =====
|
||||
cd /workspace/flux-admin
|
||||
bun install # 安装依赖
|
||||
bun run dev # 开发模式
|
||||
|
||||
# ===== flux-uniapp (UniApp) =====
|
||||
cd /workspace/flux-uniapp
|
||||
npm install
|
||||
npm run dev:h5 # H5 模式
|
||||
|
||||
# ===== Go 服务 =====
|
||||
cd /workspace/flux-mock && go run *.go
|
||||
cd /workspace/flux-test && go run test_quick.go qulaijie -mode=collide
|
||||
cd /workspace/file-server && go run *.go
|
||||
```
|
||||
|
||||
## 连接信息速查
|
||||
|
||||
详细配置见:
|
||||
- 服务器: `/root/.dev-env/servers.yaml`
|
||||
- 数据库: `/root/.dev-env/databases.yaml`
|
||||
- 规范: `/root/.dev-env/norms.md`
|
||||
|
||||
**快速参考**:
|
||||
|
||||
| 环境 | MySQL | Redis |
|
||||
|------|-------|-------|
|
||||
| 测试 (flux_dev) | 见 databases.yaml(不入库) | 见 databases.yaml :6379 |
|
||||
| 生产 (flux_prox) | 见 databases.yaml(不入库) | — |
|
||||
|
||||
## 项目知识库
|
||||
|
||||
Flux 专属文档位于 `/root/.dev-env/docs/`:
|
||||
|
||||
| 文件 | 用途 |
|
||||
|------|------|
|
||||
| Redkale使用易错点.md | **flux-api 必读** — 框架踩坑记录 |
|
||||
| MySQL慢查询分析.md | SQL 慢查询优化参考 |
|
||||
|
||||
## 开发规范
|
||||
|
||||
> **唯一事实来源**: `/root/.dev-env/norms.md`
|
||||
> 本文件不重复规范内容,避免维护不一致。
|
||||
|
||||
执行任务前先读取 norms.md,关键规则:
|
||||
- Git 提交、代码审查、命名规范、高危操作检查清单
|
||||
@@ -0,0 +1 @@
|
||||
{"permissions": {"allow": ["Bash(mysql-proxy *)", "Bash(ssh-proxy *)", "Bash(redis-proxy *)", "Bash(mongo-proxy *)", "Bash(mvn *)", "Bash(java *)", "Bash(javac *)", "Bash(go *)", "Bash(bun *)", "Bash(npm *)", "Bash(node *)", "Bash(cmd *)", "Bash(git *)", "Bash(docker *)", "Bash(curl *)", "Bash(wget *)", "Bash(cat *)", "Bash(ls *)", "Bash(grep *)", "Bash(find *)", "Read", "Write", "Edit"], "deny": []}}
|
||||
@@ -0,0 +1,124 @@
|
||||
# MySQL 慢查询分析
|
||||
|
||||
---
|
||||
|
||||
## 查看慢查询配置
|
||||
|
||||
```sql
|
||||
SHOW VARIABLES LIKE 'slow_query%';
|
||||
SHOW VARIABLES LIKE 'long_query_time';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 查询慢日志(RDS/MySQL)
|
||||
|
||||
```sql
|
||||
-- 最近24小时慢查询
|
||||
SELECT
|
||||
db,
|
||||
LEFT(REPLACE(REPLACE(sql_text, '\n', ' '), ' ', ' '), 200) as sql_preview,
|
||||
query_time,
|
||||
rows_examined,
|
||||
rows_sent,
|
||||
start_time
|
||||
FROM mysql.slow_log
|
||||
WHERE start_time > DATE_SUB(NOW(), INTERVAL 24 HOUR)
|
||||
ORDER BY query_time DESC
|
||||
LIMIT 30;
|
||||
|
||||
-- 查询所有慢记录
|
||||
SELECT * FROM mysql.slow_log ORDER BY start_time DESC LIMIT 20;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 查看当前运行的长查询
|
||||
|
||||
```sql
|
||||
-- 查看所有查询
|
||||
SHOW FULL PROCESSLIST;
|
||||
|
||||
-- 筛选长查询(超过10秒)
|
||||
SELECT
|
||||
id, User, Host, db, Time, State,
|
||||
LEFT(Info, 150) as sql_preview
|
||||
FROM information_schema.processlist
|
||||
WHERE Command = 'Query' AND Time > 10
|
||||
ORDER BY Time DESC;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 终止查询
|
||||
|
||||
```sql
|
||||
-- 单个终止
|
||||
KILL <process_id>;
|
||||
|
||||
-- 批量生成终止语句(按来源IP)
|
||||
SELECT CONCAT('KILL ', id, ';')
|
||||
FROM information_schema.processlist
|
||||
WHERE Host LIKE '39.99.243.191%' AND Command = 'Query';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## performance_schema 分析
|
||||
|
||||
```sql
|
||||
-- 按总耗时排序
|
||||
SELECT
|
||||
SCHEMA_NAME,
|
||||
LEFT(DIGEST_TEXT, 150) as sql_pattern,
|
||||
COUNT_STAR as exec_count,
|
||||
ROUND(SUM_TIMER_WAIT/1000000000000, 2) as total_sec,
|
||||
ROUND(AVG_TIMER_WAIT/1000000000, 2) as avg_ms,
|
||||
ROUND(MAX_TIMER_WAIT/1000000000000, 2) as max_sec
|
||||
FROM performance_schema.events_statements_summary_by_digest
|
||||
WHERE SCHEMA_NAME IS NOT NULL
|
||||
ORDER BY SUM_TIMER_WAIT DESC
|
||||
LIMIT 20;
|
||||
|
||||
-- 按平均耗时排序
|
||||
SELECT
|
||||
SCHEMA_NAME,
|
||||
LEFT(DIGEST_TEXT, 150) as sql_pattern,
|
||||
COUNT_STAR as exec_count,
|
||||
ROUND(AVG_TIMER_WAIT/1000000000, 2) as avg_ms,
|
||||
ROUND(MAX_TIMER_WAIT/1000000000000, 2) as max_sec
|
||||
FROM performance_schema.events_statements_summary_by_digest
|
||||
WHERE SCHEMA_NAME IS NOT NULL
|
||||
ORDER BY AVG_TIMER_WAIT DESC
|
||||
LIMIT 15;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 查看表大小
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
table_schema,
|
||||
table_name,
|
||||
ROUND(data_length/1024/1024, 2) as data_MB,
|
||||
ROUND(index_length/1024/1024, 2) as index_MB,
|
||||
table_rows
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema IN ('db1', 'db2')
|
||||
ORDER BY data_length DESC
|
||||
LIMIT 30;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 常用连接
|
||||
|
||||
| 库 | 连接命令 |
|
||||
|----|----------|
|
||||
| suke 生产库(阿里云RDS) | `mysql -hrm-8vbrd2wil14hclop3vm.mysql.zhangbei.rds.aliyuncs.com -uroot -pWFFGwffg233` |
|
||||
| 二号机 | `mysql -h47.92.117.13 -uroot -px251119!` |
|
||||
|
||||
---
|
||||
|
||||
*创建时间:2026-03-04*
|
||||
@@ -0,0 +1,382 @@
|
||||
# Redkale FilterNode 使用易错点
|
||||
|
||||
> 创建时间:2026-02-03
|
||||
> 基于:flux-api 项目实际代码
|
||||
|
||||
---
|
||||
|
||||
## ❌ 常见错误
|
||||
|
||||
### 1. LIKE 查询错误
|
||||
|
||||
**错误写法**:
|
||||
```java
|
||||
// ❌ 错误:使用静态方法
|
||||
node.and(PartnerFormRecord::getRealname, FilterNode.like(keyword));
|
||||
```
|
||||
|
||||
**正确写法**:
|
||||
```java
|
||||
// ✅ 正确:使用实例方法
|
||||
node.like(PartnerFormRecord::getRealname, keyword);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. 比较查询错误
|
||||
|
||||
**错误写法**:
|
||||
```java
|
||||
// ❌ 错误:方法名拼写错误
|
||||
node.and(PartnerFormRecord::getCreatedtime, FilterNode.greateEqual(starttime));
|
||||
node.and(PartnerFormRecord::getCreatedtime, FilterNode.lessEqual(endtime));
|
||||
```
|
||||
|
||||
**正确写法**:
|
||||
```java
|
||||
// ✅ 正确:使用简短的方法名
|
||||
node.ge(PartnerFormRecord::getCreatedtime, starttime);
|
||||
node.le(PartnerFormRecord::getCreatedtime, endtime);
|
||||
```
|
||||
|
||||
**参考方法**:
|
||||
- `ge()` - Greater or Equal(>=)
|
||||
- `le()` - Less or Equal(<=)
|
||||
- `gt()` - Greater Than(>)
|
||||
- `lt()` - Less Than(<)
|
||||
|
||||
---
|
||||
|
||||
### 3. OR 条件错误
|
||||
|
||||
**错误写法**:
|
||||
```java
|
||||
// ❌ 错误:在 and() 中混用 FilterNode 静态方法
|
||||
node.and(
|
||||
FilterNode.or(
|
||||
PartnerFormRecord::getRealname, FilterNode.like(keyword),
|
||||
PartnerFormRecord::getMobile, FilterNode.like(keyword)
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
**正确写法**:
|
||||
```java
|
||||
// ✅ 正确:创建独立 OR 节点,链式调用
|
||||
FilterNode orNode = new FilterNode();
|
||||
orNode.or(PartnerFormRecord::getRealname, FilterNode.like(keyword))
|
||||
.or(PartnerFormRecord::getMobile, FilterNode.like(keyword))
|
||||
.or(PartnerFormRecord::getIdcard, FilterNode.like(keyword));
|
||||
node.and(orNode);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4. 等值条件冗余
|
||||
|
||||
**错误写法**:
|
||||
```java
|
||||
// ❌ 冗余:等值条件不需要特殊方法
|
||||
node.and(PartnerFormRecord::getStatus, FilterNode.eq(1));
|
||||
```
|
||||
|
||||
**正确写法**:
|
||||
```java
|
||||
// ✅ 简洁:直接传值
|
||||
node.and(PartnerFormRecord::getStatus, 1);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5. NOT 条件错误
|
||||
|
||||
**错误写法**:
|
||||
```java
|
||||
// ❌ 错误:使用不存在的 notLike 方法
|
||||
node.notLike(PartnerFormRecord::getStatus, 3);
|
||||
```
|
||||
|
||||
**正确写法**:
|
||||
```java
|
||||
// ✅ 正确:使用 notEq
|
||||
node.notEq(PartnerFormRecord::getStatus, 3);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ 正确的模式
|
||||
|
||||
### 基础查询条件
|
||||
|
||||
```java
|
||||
FilterNode node = new FilterNode();
|
||||
|
||||
// 固定条件
|
||||
node.and(PartnerFormRecord::getDraftstatus, (short) 0);
|
||||
|
||||
// 可选条件
|
||||
if (bean.getKeyword() != null && !bean.getKeyword().isEmpty()) {
|
||||
node.like(PartnerFormRecord::getRealname, bean.getKeyword());
|
||||
}
|
||||
|
||||
// 范围查询
|
||||
if (bean.getStarttime() > 0) {
|
||||
node.ge(PartnerFormRecord::getCreatedtime, bean.getStarttime());
|
||||
}
|
||||
if (bean.getEndtime() > 0) {
|
||||
node.le(PartnerFormRecord::getCreatedtime, bean.getEndtime());
|
||||
}
|
||||
|
||||
// 不等于
|
||||
node.notEq(PartnerFormRecord::getStatus, (short) 3);
|
||||
```
|
||||
|
||||
### OR 条件组合
|
||||
|
||||
```java
|
||||
// 关键词:姓名 OR 电话 OR 身份证
|
||||
if (bean.getKeyword() != null && !bean.getKeyword().isEmpty()) {
|
||||
FilterNode orNode = new FilterNode();
|
||||
orNode.or(PartnerFormRecord::getRealname, FilterNode.like(bean.getKeyword()))
|
||||
.or(PartnerFormRecord::getMobile, FilterNode.like(bean.getKeyword()))
|
||||
.or(PartnerFormRecord::getIdcard, FilterNode.like(bean.getKeyword()));
|
||||
node.and(orNode);
|
||||
}
|
||||
```
|
||||
|
||||
### IN 条件
|
||||
|
||||
```java
|
||||
// IN 查询
|
||||
FilterNode node = new FilterNode();
|
||||
node.in(PartnerFormRecord::getFormdataid, formIds.toArray());
|
||||
List<PartnerFormRecord> list = dataSource.queryList(PartnerFormRecord.class, node);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📋 FilterNode 方法速查表
|
||||
|
||||
| 方法 | 说明 | 示例 |
|
||||
|------|------|------|
|
||||
| `and()` | AND 等值条件 | `node.and(Entity::getField, value)` |
|
||||
| `like()` | LIKE 模糊查询 | `node.like(Entity::getField, "%keyword%")` |
|
||||
| `ge()` | >= 大于等于 | `node.ge(Entity::getTime, timestamp)` |
|
||||
| `le()` | <= 小于等于 | `node.le(Entity::getTime, timestamp)` |
|
||||
| `gt()` | > 大于 | `node.gt(Entity::getField, value)` |
|
||||
| `lt()` | < 小于 | `node.lt(Entity::getField, value)` |
|
||||
| `notEq()` | != 不等于 | `node.notEq(Entity::getStatus, 3)` |
|
||||
| `in()` | IN 查询 | `node.in(Entity::getId, ids.toArray())` |
|
||||
| `or()` | OR 条件 | 需创建独立 FilterNode |
|
||||
|
||||
---
|
||||
|
||||
## 🔍 完整示例参考
|
||||
|
||||
### PartnerFormService.java
|
||||
|
||||
```java
|
||||
@RestMapping(name = "list", comment = "分页查询表单记录")
|
||||
public RetResult<Sheet<PartnerFormRecord>> list(FormRecordFilterBean bean, Flipper flipper) {
|
||||
FilterNode node = new FilterNode();
|
||||
node.notEq(PartnerFormRecord::getStatus, STATUS_DELETED);
|
||||
|
||||
if (!Utils.isEmpty(bean.getRealname())) {
|
||||
node.like(PartnerFormRecord::getRealname, bean.getRealname());
|
||||
}
|
||||
if (bean.getDraftstatus() >= 0) {
|
||||
node.and(PartnerFormRecord::getDraftstatus, bean.getDraftstatus());
|
||||
}
|
||||
if (bean.getStarttime() > 0) {
|
||||
node.ge(PartnerFormRecord::getCreatedtime, bean.getStarttime());
|
||||
}
|
||||
if (bean.getEndtime() > 0) {
|
||||
node.le(PartnerFormRecord::getCreatedtime, bean.getEndtime());
|
||||
}
|
||||
|
||||
Sheet<PartnerFormRecord> sheet = dataSource.querySheet(PartnerFormRecord.class, flipper, node);
|
||||
return render(sheet);
|
||||
}
|
||||
```
|
||||
|
||||
### PartnerCustomerService.java
|
||||
|
||||
```java
|
||||
@RestMapping(name = "list", comment = "分页查询客户列表")
|
||||
public RetResult<Sheet<PartnerCustomer>> list(PartnerCustomerFilterBean bean, Flipper flipper) {
|
||||
FilterNode node = new FilterNode();
|
||||
node.notEq(PartnerCustomer::getStatus, (short) 3);
|
||||
|
||||
if (!Utils.isEmpty(bean.getMobile())) {
|
||||
node.and(PartnerCustomer::getMobile, bean.getMobile());
|
||||
}
|
||||
if (bean.getStatus() > 0) {
|
||||
node.and(PartnerCustomer::getStatus, bean.getStatus());
|
||||
}
|
||||
if (bean.getStarttime() > 0) {
|
||||
node.ge(PartnerCustomer::getCreatedtime, bean.getStarttime());
|
||||
}
|
||||
if (bean.getEndtime() > 0) {
|
||||
node.le(PartnerCustomer::getCreatedtime, bean.getEndtime());
|
||||
}
|
||||
|
||||
Sheet<PartnerCustomer> sheet = dataSource.querySheet(PartnerCustomer.class, flipper, node);
|
||||
return render(sheet);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 注意事项
|
||||
|
||||
1. **方法调用方式**:所有条件方法都是 `FilterNode` 实例的方法,不是静态方法
|
||||
2. **方法命名**:`ge`/`le` 而非 `greateEqual`/`lessEqual`
|
||||
3. **OR 条件**:必须创建独立的 `FilterNode` 对象进行链式调用
|
||||
4. **值比较**:等值条件直接传值,不需要 `FilterNode.eq()`
|
||||
5. **LIKE 参数**:通常需要在参数中自行添加 `%` 通配符
|
||||
|
||||
---
|
||||
|
||||
## 📚 参考代码位置
|
||||
|
||||
- `flux-api/src/main/java/cn/casehub/partner/PartnerFormService.java`
|
||||
- `flux-api/src/main/java/cn/casehub/partner/PartnerCustomerService.java`
|
||||
- `flux-api/src/main/java/cn/casehub/partner/PartnerVisitService.java`
|
||||
|
||||
---
|
||||
|
||||
# Redkale JSON 使用要点
|
||||
|
||||
> 更新时间:2026-03-03
|
||||
> 背景:速贷接口接入时错误使用 `@JsonProperty`(Jackson 注解),Redkale 不支持
|
||||
|
||||
---
|
||||
|
||||
## ❌ 常见错误
|
||||
|
||||
### 1. 使用 Jackson 注解
|
||||
|
||||
**错误写法**:
|
||||
```java
|
||||
// ❌ 错误:Redkale 不支持 Jackson 注解
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
@JsonProperty("mobile_md5")
|
||||
private String mobileMd5;
|
||||
```
|
||||
|
||||
**正确方案**:使用 Redkale 的 `@ConvertColumn(name = "xxx")` 进行字段重命名:
|
||||
|
||||
```java
|
||||
// ✅ 正确:使用 @ConvertColumn 重命名
|
||||
import org.redkale.convert.ConvertColumn;
|
||||
|
||||
@ConvertColumn(name = "mobile_md5")
|
||||
private String mobileMd5; // Java驼峰,JSON下划线
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ JsonConvert 使用方式
|
||||
|
||||
### 序列化(对象 → JSON)
|
||||
|
||||
```java
|
||||
// 方式1:实例方法
|
||||
JsonConvert convert = JsonConvert.root();
|
||||
String json = convert.convertTo(object);
|
||||
|
||||
// 方式2:静态调用
|
||||
String json = JsonConvert.root().convertTo(object);
|
||||
```
|
||||
|
||||
### 反序列化(JSON → 对象)
|
||||
|
||||
```java
|
||||
// 单个对象
|
||||
User user = JsonConvert.root().convertFrom(User.class, json);
|
||||
|
||||
// 泛型集合
|
||||
List<User> users = JsonConvert.root().convertFrom(
|
||||
new TypeToken<List<User>>() {}.getType(),
|
||||
json
|
||||
);
|
||||
|
||||
// Map(用于解析不确定结构的JSON)
|
||||
Map<String, Object> map = JsonConvert.root().convertFrom(Map.class, json);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📋 字段映射规则
|
||||
|
||||
| Java 字段名 | JSON 字段名 | 说明 |
|
||||
|------------|------------|------|
|
||||
| `mobileMd5` | `mobileMd5` | 默认:驼峰 → 驼峰 |
|
||||
| `mobile_md5` | `mobile_md5` | 默认:下划线 → 下划线 |
|
||||
| `mobileMd5` + `@ConvertColumn(name="mobile_md5")` | `mobile_md5` | ✅ 重命名 |
|
||||
| `mobileMd5` + `@ConvertColumn(ignore=true)` | - | 忽略字段 |
|
||||
|
||||
**结论**:使用 `@ConvertColumn(name = "xxx")` 可实现字段重命名。
|
||||
|
||||
---
|
||||
|
||||
## 🔧 跨命名风格转换方案
|
||||
|
||||
当第三方接口使用下划线,内部使用驼峰时:
|
||||
|
||||
### 方案1:@ConvertColumn + Utils.copy(推荐)
|
||||
|
||||
```java
|
||||
// 速贷请求Bean(Java驼峰,JSON下划线)
|
||||
public class SuDaiRequest {
|
||||
@ConvertColumn(name = "mobile_md5")
|
||||
private String mobileMd5;
|
||||
|
||||
@ConvertColumn(name = "city_id")
|
||||
private String cityId;
|
||||
}
|
||||
|
||||
// 内部Bean(驼峰)
|
||||
public class CollideBean {
|
||||
private String mobileMd5;
|
||||
private String cityId;
|
||||
}
|
||||
|
||||
// 转换器 - 使用 Utils.copy
|
||||
public class Converter {
|
||||
public static CollideBean toBean(SuDaiRequest req) {
|
||||
return Utils.copy(new CollideBean(), req); // 一行搞定
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 方案2:手动映射(不推荐)
|
||||
|
||||
```java
|
||||
// 速贷请求Bean(下划线)
|
||||
public class SuDaiRequest {
|
||||
private String mobile_md5;
|
||||
private String city_id;
|
||||
}
|
||||
|
||||
// 转换器 - 手动映射
|
||||
public class Converter {
|
||||
public static CollideBean toBean(SuDaiRequest req) {
|
||||
CollideBean bean = new CollideBean();
|
||||
bean.setMobileMd5(req.getMobile_md5()); // 手动映射
|
||||
bean.setCityId(req.getCity_id());
|
||||
return bean;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 注意事项
|
||||
|
||||
1. **使用 @ConvertColumn**:Redkale 的字段重命名注解,不是 Jackson 的 @JsonProperty
|
||||
2. **@ConvertColumn 用法**:
|
||||
- `@ConvertColumn(name = "xxx")` - 重命名JSON字段
|
||||
- `@ConvertColumn(ignore = true)` - 忽略字段
|
||||
3. **配合 Utils.copy**:重命名后,Java字段名一致,可用 `Utils.copy` 简化转换
|
||||
@@ -0,0 +1,29 @@
|
||||
# mysql-proxy 连接配置模板
|
||||
# 用法:复制为 mysql-proxy.toml 后填入真实值(mysql-proxy.toml 已在 .gitignore,不入库)
|
||||
# 真实密码/IP 请勿提交,参见 docs/03-运维/开发环境搭建手册.md
|
||||
|
||||
[server]
|
||||
port = 3307
|
||||
host = "0.0.0.0"
|
||||
|
||||
[pool]
|
||||
default_max_connections = 5
|
||||
idle_timeout_secs = 300
|
||||
check_interval_secs = 60
|
||||
|
||||
[[connections]]
|
||||
name = "flux_dev"
|
||||
host = "<TEST_DB_HOST>"
|
||||
port = 3306
|
||||
user = "root"
|
||||
password = "<TEST_DB_PASSWORD>"
|
||||
database = "flux_dev"
|
||||
|
||||
[[connections]]
|
||||
name = "flux_prox"
|
||||
host = "<PROD_DB_HOST>"
|
||||
port = 3306
|
||||
user = "root"
|
||||
password = "<PROD_DB_PASSWORD>"
|
||||
database = "flux_prox"
|
||||
max_connections = 10
|
||||
@@ -0,0 +1,102 @@
|
||||
# Flux 项目开发规范
|
||||
|
||||
> 本文件供 Claude Code / AI 助手阅读,统一开发规范认知
|
||||
|
||||
---
|
||||
|
||||
## Git 提交规范
|
||||
|
||||
```
|
||||
<类型>: <简述>
|
||||
```
|
||||
|
||||
**类型**: 新增 | 修复 | 优化 | 重构
|
||||
|
||||
**禁止**:
|
||||
- 英文提交信息
|
||||
- Co-Authored-By 尾巴
|
||||
- 提及工具名称(如 "generated by Claude")
|
||||
|
||||
**示例**:
|
||||
```
|
||||
新增: 渠道价格匹配规则引擎
|
||||
修复: 下游进件超时未回调处理
|
||||
优化: SQL 慢查询添加联合索引
|
||||
重构: UpstreamService 抽象三方对接接口
|
||||
```
|
||||
|
||||
## 技术栈与启动命令
|
||||
|
||||
| 模块 | 技术 | 目录 | 启动命令 |
|
||||
|------|------|------|---------|
|
||||
| flux-api | Java 17 + Redkale + Maven | flux-api/ | `cmd /c bin\restart.bat` 或 `mvn clean package` 后运行 |
|
||||
| flux-admin | Vue 3 + Arco Design Pro + Vite + bun | flux-admin/ | `bun install && bun run dev` |
|
||||
| flux-uniapp | UniApp (H5) + Vite | flux-uniapp/ | `npm install && npm run dev:h5` |
|
||||
| flux-mock | Go (模拟三方) | flux-mock/ | `go run *.go` |
|
||||
| flux-test | Go (业务链测试) | flux-test/ | `go run test_quick.go {channel}` |
|
||||
| file-server | Go (文件上传) | file-server/ | `go run *.go` |
|
||||
| flux-wechat-api | 微信服务 | flux-wechat-api/ | — |
|
||||
|
||||
## 核心架构
|
||||
|
||||
```
|
||||
上游渠道 (10个) → UpstreamService → 下游对接 (美信/小薇/易贷通/CryptoApi) → 三方机构
|
||||
```
|
||||
|
||||
- **上游**: 小爱、速贷、趣来借、臻品借、微融花、腰贷钱包、演示、放心借、龙享花、可贷
|
||||
- **下游**: 美信钱包、美信B、小薇钱包、易贷通、有鑫钱包、源融花、闪融花、Mock
|
||||
- **加密**: AES-CBC/ECB, CBC模式, 部分Base64密钥
|
||||
|
||||
## 代码审查要点
|
||||
|
||||
### Redkale 框架 (flux-api)
|
||||
- FilterNode 使用规范 → 见 `/root/.kms/Redkale使用易错点.md`
|
||||
- JSON 字段映射用 `@ConvertColumn`,注意命名转换
|
||||
- 数据库连接通过 `@Resource(name="lake")` 注入
|
||||
- 自定义 Render 用 `cn.casehub.base.TplRender`
|
||||
|
||||
### 前端 (flux-admin)
|
||||
- 组件库: Arco Design Pro
|
||||
- 状态管理: Pinia
|
||||
- 路由: Vue Router 4
|
||||
|
||||
### Go 服务
|
||||
- 交叉编译: `$env:GOOS="linux"; $env:GOARCH="amd64"; go build`
|
||||
- 配置文件: 各模块独立 config.json
|
||||
|
||||
## 命名规范
|
||||
|
||||
```
|
||||
{业务域}-{类型}-{名称}
|
||||
```
|
||||
|
||||
| 前缀 | 含义 |
|
||||
|------|------|
|
||||
| flux | Flux 架构核心项目 |
|
||||
|
||||
| 后缀 | 含义 |
|
||||
|------|------|
|
||||
| -api | 后端 API 服务 |
|
||||
| -admin | 管理后台前端 |
|
||||
| -web | 用户端 Web |
|
||||
| -mp | 小程序 |
|
||||
| -task | 定时任务 |
|
||||
| -kit | 工具库/SDK |
|
||||
|
||||
## 高危操作检查清单
|
||||
|
||||
- [ ] 改 Docker/系统配置 → 先备份,验证语法,再重启
|
||||
- [ ] 删数据库/容器 → 确认备份和影响范围
|
||||
- [ ] 生产环境操作 → 必须先在测试环境验证
|
||||
- [ ] 发版部署 → 通知相关人,准备回滚方案
|
||||
- [ ] 数据库 DML → 先 SELECT 预览,确认 WHERE 条件
|
||||
|
||||
## 模型选择参考
|
||||
|
||||
| 场景 | 推荐模型 |
|
||||
|------|---------|
|
||||
| 关键决策/技术选型 | glm-5 / deepseek-r1 |
|
||||
| 复杂推理 | deepseek-r1 |
|
||||
| 图片理解 | kimi-k2.5 / qwen-max |
|
||||
| 日常开发操作 | kimi-k2.5 / deepseek-v3 |
|
||||
| 简单任务 | glm-4.7-flash / qwen-turbo |
|
||||
@@ -0,0 +1,17 @@
|
||||
# redis-proxy 连接配置模板
|
||||
# 用法:复制为 redis-proxy.toml 后填入真实值(redis-proxy.toml 已在 .gitignore,不入库)
|
||||
|
||||
[server]
|
||||
port = 3310
|
||||
host = "0.0.0.0"
|
||||
|
||||
[pool]
|
||||
idle_timeout_secs = 300
|
||||
check_interval_secs = 60
|
||||
|
||||
[[connections]]
|
||||
name = "flux_dev"
|
||||
host = "<TEST_REDIS_HOST>"
|
||||
port = 6379
|
||||
password = "<REDIS_PASSWORD>"
|
||||
db = 0
|
||||
@@ -0,0 +1,25 @@
|
||||
# ssh-proxy 连接配置模板
|
||||
# 用法:复制为 ssh-proxy.toml 后填入真实值(ssh-proxy.toml 已在 .gitignore,不入库)
|
||||
# 注意:server name 用短名(如 byr_pro),作为内部连接标识符
|
||||
|
||||
[server]
|
||||
port = 3308
|
||||
host = "0.0.0.0"
|
||||
|
||||
[pool]
|
||||
idle_timeout_secs = 300
|
||||
check_interval_secs = 60
|
||||
|
||||
[[servers]]
|
||||
name = "flux_dev"
|
||||
host = "<TEST_SERVER_HOST>"
|
||||
port = 22
|
||||
user = "root"
|
||||
private_key = "/root/.ssh/id_ed25519"
|
||||
|
||||
[[servers]]
|
||||
name = "byr_pro"
|
||||
host = "<PROD_SERVER_HOST>"
|
||||
port = 22
|
||||
user = "root"
|
||||
private_key = "/root/.ssh/id_ed25519"
|
||||
Reference in New Issue
Block a user