# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
This is a Fxiaoke CRM (Customer Relationship Management) project containing custom business logic and automation scripts for the Fxiaoke SaaS platform. The project includes over 1400 Groovy scripts that handle various CRM operations.
Key Technologies
-
Language: Groovy (primarily for business logic scripts)
-
IDE: Visual Studio Code with Fxiaoke ShareDev extension
-
Platform: Fxiaoke CRM (https://www.fxiaoke.com)
Codebase Structure
CRMAPLDev/
├── package/fx/custom/apl/script/ # Main Groovy scripts directory (1443 files)
├── lib/ # Libraries and tools
│ ├── docs/ # API documentation and references
│ │ └── 函数API.md # Fx framework API reference (Chinese)
│ └── tools/ # Development tools
│ └── tasks.js # Obsidian Tasks plugin configuration
├── .vscode/ # VS Code configuration
│ ├── settings.json # Workspace settings
│ ├── document/ # Documentation files ingnore this folder
│ └── object/ # CRM object definitions
└── .claude/ # Claude Code configuration
Development Workflow
Code Editing
Only modify the current file, which means that when I make a request, you only need to write logic in the current file.
Fxiaoke CRM API Usage
Common API methods used in scripts:
API Documentation: See API文档 for complete Fx framework API reference.
Object Fields Documentation:See 对象字段说明 for CRM object and field definitions.
Business Object Mapping Table
Here are some common objects and their API names: See 对象字段说明 for complete mapping.
function types explanation
-
Button Functions: Triggered by UI buttons. Query the data of related objects through the current object data, and then update or create a new one according to the corresponding business scenario.
-
Scheduled Task Functions: Run on a schedule, defined in the Fxiaoke platform. These can be more complex and include error handling as needed.
Button Function Style
For button functions in package/fx/custom/apl/script/ directory:
-
Do NOT use
def action()wrapper - Write logic directly at the top level -
Do NOT use try-catch blocks - Handle errors using simple if-else statements
-
Keep it simple and concise - Follow the style of existing button functions
Good Example:
/**
* @author 王亚新
* @codeName 【IT】复制费用-大区
* @description 生成新的大区
* @createTime 2025-12-26
* @bindingObjectLabel 费用-大区
* @bindingObjectApiName object_G1rnx__c
* @函数需求编号
*/
String id = context.data._id as String //id
String bigArea = context.data.field_248Cd__c as String //大区
List ownerList = context.data.field_6GINq__c as List //个人(财务共享)
String areaCode = context.data.field_w2aCs__c as String //大区编码 --
String orgName = context.data.field_IH27f__c as String //组织
areaCode = areaCode.replace("2025", "")
Map masterData = [
"field_248Cd__c": bigArea,
"field_6GINq__c": ownerList,
"field_w2aCs__c": areaCode,
"field_IH27f__c": orgName
]
def ret = Fx.object.create("object_G1rnx__c", masterData, null, CreateAttribute.builder().build()).result() as Map
String dataId = ret["data"]["_id"] as String
def updateRet = Fx.object.update("object_G1rnx__c", id, ["transfer_storage__c": dataId], UpdateAttribute.builder().triggerWorkflow(true).build())
Scheduled Task Function Style
- Use the following fixed template for scheduled functions:
/**
* @author [Author Name]
* @codeName [Code Name]
* @description [Function Description]
* @createTime [Creation Time]
*/
List ids = context.objectIds as List // 获取调度任务传入的对象ID列表
List neededFields = ["_id", "name"] // 根据业务需求调整需要查询的字段
def (boolean errorG, List dataList, String errorMessageG) = Fx.object.findByIds("object_G1rnx__c", ids, FQLAttribute.builder().columns(neededFields).build())
dataList.each {
item ->
// 业务逻辑代码
}
Naming Conventions
Variable Naming
| 类型 | 规范 | 示例 |
| ------ | ---------------- | ------------------------------------ |
| 方法名 | 动词/动宾,首字母小写驼峰 | getValue(), calculateTotal() |
| 常量 | 全大写,下划线分隔 | DEFAULT_TIMEOUT, MAX_CONNECTIONS |
| 变量名 | 首字母小写驼峰,避免单字母 | orderList, customerName |
| 代码块级变量 | 双下划线起始,全小写,下划线分隔 | __data_id, __data_name |
Good variable naming example:
class CYGUtil {
static private List errorFind = []; //错误查询数据
/**
* @Description 查询电话为110的对象数据
* @param List dataList 对象数组
* @return List 满足条件的对象数组
*/
static List printLog(List dataList) {
List resultData = [] as List
dataList.each{
item ->
Map __data_map = item as Map
String __user_phone = __data_map["phone"] as String
if(__user_phone == "110"){
resultData.add(__data_map)
}
}
log.info("返回结果日志:" + resultData)
return resultData
}
}
Comment Standards
General Principles
-
Clarity First - Comments should explain why, not what. The code itself should show what it does.
-
Language - Use Chinese for comments as this is a domestic project.
-
Keep Comments Current - Update comments when code changes. Outdated comments are worse than no comments.
File Header Comments
Every source file should contain a file header comment block:
/**
* [文件名] - [功能描述]
*
* 创建时间: [DATE]
* 功能说明: [详细功能描述]
* @author Yaxin Wang
*/
Inline Comments
// 单行注释:说明复杂逻辑或业务规则
// 转换为万元单位(元 -> 万元)
def amountInWan = amountInYuan / 10000
Field/Object Comments
// field_22C8p__c - 合同金额(元)
// object_hrnjC__c - 合同政策&费用拆分对象
TODO Comments
// TODO: [待办事项描述] - [作者名] - [日期]
// TODO: 优化费用分配算法,支持动态节点配置 - Yaxin Wang - 2024-01-22
Comment Content Guidelines
Good Comments :
// 新产品认定规则:省网配网项目或电压等级>=220kV
def isNewProduct = isProvincialNetwork || voltageLevel >= 220
// 银行家舍入法:避免四舍五入导致的分配不平衡
def distributed = bankersRounding(amount, nodes)
Bad Comments :
// 设置i为1 // ❌ 代码已经很清楚了
def i = 1
// 调用方法 // ❌ 没有提供任何有用信息
someMethod()
Business Logic Comments
For complex business logic, use region/endregion markers:
// region >>> Yaxin Wang [省网辅控费用分配] ===========
// 此处处理省网辅控&配网中标费用的分配逻辑
// ... 业务逻辑代码 ...
// endregion <<< Yaxin Wang [省网辅控费用分配] ===========
Marker Format / 标记格式说明:
-
region >>> [Author] [Description] =========== -
endregion <<< [Author] [Description] =========== -
Author name in Pinyin or English, e.g.:
Yaxin Wang -
Description should be concise, e.g.:
[费用分配],[新产品认定],[政策匹配]