How AI Can Co‑Create a Query‑Logging Feature: Two Paths, One Result

A test‑developer explores how AI can design and implement a query‑recording function for an insurance policy platform, comparing a code‑savvy approach with a low‑code approach, detailing architecture, AOP interception, async handling, code generation, review, and testing considerations.

JD Tech
JD Tech
JD Tech
How AI Can Co‑Create a Query‑Logging Feature: Two Paths, One Result

Opportunity and Requirement Background

The Policy Command Center platform is a unified data query system covering the entire insurance business lifecycle. A new requirement emerged: record every user query for data tracing and performance monitoring without impacting user experience.

What information to record? (user, time, parameters, response)

How to record without affecting main‑service performance?

Where to store the data and for how long?

How to design for future extensibility?

Two Perspectives on Solving the Problem

Perspective A – Code‑Savvy Engineer

"I know AOP can provide aspect interception, async processing avoids blocking the main flow, and design patterns should follow single‑responsibility… let AI turn these ideas into concrete code."

Input to AI (plain text, no HTML tags):

【Project Background】
Policy Command Center – unified data query platform for insurance lifecycle
Tech Stack: Front‑end Vue, Back‑end DongBoot (dual‑service architecture)
【Service Roles】
| Service | Role | Analogy |
|---|---|---|
| Service A (API Gateway) | Front desk | Receives requests, validates, routes |
| Service B (Business Service) | Business clerk | Executes business logic, aggregates data |
【Requirement】
Implement a "query‑trace" feature that records:
• User ERP
• Query type
• Request URI
• Request parameters
• Response data
• Execution time
Constraints: async processing, data integrity, decide which layer (A/B/both) should handle logging.

AI‑Generated Architecture

┌───────────────────────────────┐
│ Front‑end (Vue)                 │
│  ┌─────┐  ┌─────┐  ┌─────┐      │
│  │Policy│ │Claim│ │Contract│   │
│  └─────┘  └─────┘  └─────┘      │
└───────────────┬─────────────────┘
                │ HTTP/HTTPS
┌────────────────▼─────────────────┐
│ Service A (Gateway)             │
│   QueryRecordAspect (AOP)      │
│   @RecordQuery                 │
│   ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│   │Request intercept│ │Response record│ │Exception handle│
│   └─────────────┘ └─────────────┘ └─────────────┘
│   @RecordQuery
└───────────────┬─────────────────┘
                ▼
│ QueryRecordService (async)      │
│   ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│   │Param convert│ │Async save   │ │Error handle │
│   └─────────────┘ └─────────────┘ └─────────────┘
│   MyBatis → query_record table
└─────────────────────────────────┘

Core Design Decisions

AOP Interception : Define a custom @RecordQuery annotation to mark methods; the aspect intercepts calls with zero intrusion.

Async Processing : Record saving runs in a dedicated thread pool (core 5, max 10, queue 1000) to avoid blocking the main request.

Layered Design : Separate BO/DO objects follow DDD principles; the gateway layer handles logging, while the business layer remains focused on core logic.

Key Code Artifacts

@Data
public class QueryRecordDO {
    // fields: userErp, queryType, requestUri, requestParams, responseData, durationMs, status
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RecordQuery {
    QueryType queryType();
    boolean async() default true;
    boolean recordResponse() default true;
    int maxResponseLength() default 10000;
}
@Aspect
@Component
@Order(2)
public class QueryRecordAspect {
    @Autowired private QueryRecordService queryRecordService;

    @Around("@annotation(com.example.RecordQuery)")
    public Object recordQuery(ProceedingJoinPoint joinPoint) throws Throwable {
        RecordQuery rq = ((MethodSignature) joinPoint.getSignature()).getMethod().getAnnotation(RecordQuery.class);
        QueryRecordBO bo = createBO(joinPoint, rq);
        long start = System.currentTimeMillis();
        try {
            Object result = joinPoint.proceed();
            bo.setStatus("SUCCESS");
            bo.setDuration(System.currentTimeMillis() - start);
            return result;
        } catch (Exception e) {
            bo.setStatus("FAILED");
            bo.setErrorMessage(e.getMessage());
            throw e;
        } finally {
            queryRecordService.saveQueryRecordAsync(bo);
        }
    }
}
@Configuration
@EnableAsync
public class AsyncConfig {
    @Bean("queryRecordExecutor")
    public Executor queryRecordExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(1000);
        executor.setThreadNamePrefix("QueryRecord-");
        executor.setRejectedExecutionHandler(new CallerRunsPolicy());
        return executor;
    }
}
@PostMapping("/query")
@ResponseBody
@RecordQuery(queryType = QueryType.POLICY_MATCH, recordResponse = true)
public Result<List<PolicyVo>> query(@RequestBody PolicyParamVo param) {
    return policyDomainService.queryPolicyCommand(param);
}

Review and Iteration (Code‑Savvy Path)

During code review it was discovered that the client‑IP extraction only handled the X‑Forwarded‑For header. The following improvement was requested:

"Need to handle multi‑proxy cases, add X‑Real‑IP and Proxy‑Client‑IP processing."
private String getClientIp(HttpServletRequest request) {
    String ip = request.getHeader("X-Forwarded-For");
    if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
        ip = request.getHeader("X-Real-IP");
    }
    if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
        ip = request.getHeader("Proxy-Client-IP");
    }
    if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
        ip = request.getHeader("WL-Proxy-Client-IP");
    }
    if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
        ip = request.getRemoteAddr();
    }
    if (ip != null && ip.contains(",")) {
        ip = ip.substring(0, ip.indexOf(",")).trim();
    }
    return ip;
}

Perspective B – Limited Coding Ability

"I don’t understand AOP or thread‑pool configuration, but I know the problem to solve."

The business requirement was expressed in plain language and AI proposed the same AOP‑based solution. Follow‑up questions were asked:

"If logging fails, will the user query be affected?"

"What if the response payload is several megabytes?"

"The try‑catch‑finally ensures failures are logged without breaking the main flow; the maxResponseLength parameter truncates large responses (default 10 000 characters)."

Outcome and Benefits

Complete Recording : Every query logs user, parameters, response, and latency.

High Performance : Asynchronous handling guarantees zero impact on the primary request path.

Easy Extensibility : Adding a new query type only requires a new enum value and annotating the controller method.

Maintainable Structure : Clear separation of concerns aligns with DDD principles.

Extending the Feature

// Add a new query type
YB_CUSTOM("YB_CUSTOM", "Custom business query")

// Use it
@RecordQuery(queryType = QueryType.YB_CUSTOM, recordResponse = true)
public Result<YBOrderInfoDTO> queryYbCustomInfo(...) { }

Testing‑Developer Insights

The testing background helped shape the conversation with AI:

Identifying edge cases (exception handling, large payloads, concurrency).

Defining acceptance criteria (completeness, performance, fault‑tolerance).

Designing test scenarios to validate AI‑generated code.

By iterating on AI output, the final implementation satisfied both functional and non‑functional requirements while keeping the codebase clean and extensible.

code generationArchitectureAIAOPTestingbackend developmentAsync
JD Tech
Written by

JD Tech

Official JD technology sharing platform. All the cutting‑edge JD tech, innovative insights, and open‑source solutions you’re looking for, all in one place.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.