刘帅
2026-09-21 620a310bdcfb1d8f1b49bf163413afeb522e1b2f
cloud-server-management/src/main/java/com/dsh/guns/modular/system/controller/code/TicketController.java
@@ -3,16 +3,20 @@
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.dsh.course.feignClient.account.AppUserClient;
import com.dsh.course.feignClient.account.CityManagerClient;
import com.dsh.course.feignClient.account.StoreStaffClient;
import com.dsh.course.feignClient.account.model.CityManager;
import com.dsh.course.feignClient.account.model.QueryByNamePhone;
import com.dsh.course.feignClient.account.model.TAppUser;
import com.dsh.course.feignClient.account.model.TStoreStaff;
import com.dsh.course.feignClient.activity.CouponClient;
import com.dsh.course.feignClient.activity.PointMercharsClient;
import com.dsh.course.feignClient.activity.model.*;
import com.dsh.guns.config.UserExt;
import com.dsh.guns.modular.system.audit.ManagementAuditWriter;
import com.dsh.guns.core.base.tips.SuccessTip;
import com.dsh.guns.core.common.constant.factory.PageFactory;
import com.dsh.guns.core.util.ToolUtil;
@@ -22,6 +26,8 @@
import com.dsh.guns.modular.system.service.IStoreService;
import com.dsh.guns.modular.system.util.OBSUploadUtil;
import com.dsh.guns.modular.system.util.ResultUtil;
import net.bytebuddy.asm.Advice;
import org.omg.CORBA.PRIVATE_MEMBER;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
@@ -31,10 +37,7 @@
import javax.annotation.Resource;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.stream.Collectors;
@@ -64,6 +67,10 @@
    @Resource
    private PointMercharsClient pointMercharsClient;
    @Autowired
    private StoreStaffClient storeStaffClient;
    @Resource
    private ManagementAuditWriter auditWriter;
    /**
     * 跳转到优惠券管理首页
@@ -115,8 +122,53 @@
    @RequestMapping("/changeState")
    @ResponseBody
    public Object changeState(@RequestBody CoachChangeStateVO vo){
        pointMercharsClient.changeState(vo);
        return ResultUtil.success();
        List<Map<String, Object>> before = new ArrayList<Map<String, Object>>();
        for (Integer id : vo.getIds()) {
            PointsMerchandise ticket = pointMercharsClient.getById(id);
            Map<String, Object> item = new HashMap<String, Object>();
            item.put("id", id);
            item.put("name", ticket == null ? null : ticket.getName());
            item.put("state", ticket == null ? null : ticket.getState());
            before.add(item);
        }
        try {
            pointMercharsClient.changeState(vo);
            List<Map<String, Object>> after = new ArrayList<Map<String, Object>>();
            for (Map<String, Object> item : before) {
                Map<String, Object> changed = new HashMap<String, Object>(item);
                changed.put("state", vo.getState());
                after.add(changed);
            }
            appendTicketStateAudit(vo, before, after, "SUCCESS", null);
            return ResultUtil.success();
        } catch (RuntimeException ex) {
            appendTicketStateAudit(vo, before, null, "FAIL", ex.getMessage());
            throw ex;
        }
    }
    private void appendTicketStateAudit(CoachChangeStateVO request, Object before, Object after, String result, String error) {
        try {
            Map<String, Object> audit = new HashMap<String, Object>();
            audit.put("sourceService", "mb-cloud-management");
            audit.put("sourceType", "HTTP");
            audit.put("operationType", "STATUS_CHANGE");
            audit.put("riskLevel", "CRITICAL");
            audit.put("operationName", "管理员修改门票上下架状态");
            audit.put("requestMethod", "POST");
            audit.put("requestUri", "/ticket/changeState");
            audit.put("requestParams", "ids=" + request.getIds() + ", targetState=" + request.getState());
            audit.put("beforeData", JSONObject.toJSONString(before));
            audit.put("afterData", JSONObject.toJSONString(after));
            audit.put("userId", UserExt.getUser() == null ? null : UserExt.getUser().getId());
            audit.put("userAccount", UserExt.getUser() == null ? null : UserExt.getUser().getAccount());
            audit.put("result", result);
            audit.put("errorMessage", error);
            audit.put("createdAt", new Date());
            auditWriter.append(audit);
        } catch (Exception ignored) {
            // 审计故障不影响实际的门票状态变更;由网关审计作为兜底记录。
        }
    }
    /**
     * 获取门票列表
@@ -134,6 +186,15 @@
        integralGoodsOfSearch.setActiveStatus(activeStatus);
        integralGoodsOfSearch.setState(state);
        integralGoodsOfSearch.setPage(page);
        integralGoodsOfSearch.setObjType(UserExt.getUser().getObjectType());
        integralGoodsOfSearch.setObjId(UserExt.getUser().getObjectId());
        if (UserExt.getUser().getObjectType()==2){
            // 查询这个运营商下的所有门店
            List<Integer> operatorId = storeService.list(new QueryWrapper<TStore>()
                    .eq("operatorId", UserExt.getUser().getObjectId())).stream().map(TStore::getId)
                    .collect(Collectors.toList());
            integralGoodsOfSearch.setStoreIds(operatorId);
        }
        return pointMercharsClient.ticketList(integralGoodsOfSearch);
    }
@@ -340,8 +401,80 @@
        System.out.println(b);
        return new SuccessTip<>();
    }
    /**
     * 根据管理员选择的入园方式核销门票,并写入管理端审计日志。
     *
     * @param id 用户已购门票记录ID
     * @param goType 入园方式:1=手环,2=门票核销
     */
    @RequestMapping("/writeOff")
    @ResponseBody
    public Object writeOff(String id, Integer goType) {
        WriteOffDTO dto = new WriteOffDTO();
        dto.setGoType(goType);
        try {
            if (!Integer.valueOf(1).equals(goType) && !Integer.valueOf(2).equals(goType)) {
                appendTicketWriteOffAudit(dto, "FAIL", "入园方式参数不正确");
                return ResultUtil.error("入园方式参数不正确");
            }
            dto.setId(Long.valueOf(id));
            // 核销人员ID与所属门店由登录上下文确定,不能由前端传入。
            Integer objectId = UserExt.getUser().getId();
            if (UserExt.getUser().getObjectType() == 1){
                dto.setVerifiStoreId(null);
            }else if (UserExt.getUser().getObjectType() == 2){
                TStore store = storeService.getOne(new QueryWrapper<TStore>().eq("cityManagerId", objectId));
                dto.setVerifiStoreId(store == null ? null : store.getId());
            }else{
                TStoreStaff storeByStoreStaffId = storeStaffClient.getStoreByStoreStaffId(objectId);
                dto.setVerifiStoreId(storeByStoreStaffId.getStoreId());
            }
            dto.setVerificationUserId(objectId);
            pointMercharsClient.writeOff(dto);
            appendTicketWriteOffAudit(dto, "SUCCESS", null);
            return new SuccessTip<>();
        } catch (RuntimeException ex) {
            appendTicketWriteOffAudit(dto, "FAIL", ex.getMessage());
            throw ex;
        }
    }
    /** 记录管理员核销门票及所选入园方式,审计失败不影响正常业务流程。 */
    private void appendTicketWriteOffAudit(WriteOffDTO dto, String result, String errorMessage) {
        try {
            Map<String, Object> beforeData = new HashMap<>();
            beforeData.put("id", dto.getId());
            beforeData.put("status", "待核销");
            Map<String, Object> afterData = new HashMap<>(beforeData);
            afterData.put("status", "已核销");
            afterData.put("goType", dto.getGoType());
            afterData.put("goTypeName", Integer.valueOf(1).equals(dto.getGoType()) ? "手环" : "门票核销");
            afterData.put("verificationUserId", dto.getVerificationUserId());
            afterData.put("verifiStoreId", dto.getVerifiStoreId());
            Map<String, Object> audit = new HashMap<>();
            audit.put("traceId", UUID.randomUUID().toString().replace("-", ""));
            audit.put("sourceService", "mb-cloud-management");
            audit.put("sourceType", "HTTP");
            audit.put("operationType", "STATUS_CHANGE");
            audit.put("riskLevel", "CRITICAL");
            audit.put("operationName", "管理员核销门票");
            audit.put("requestMethod", "POST");
            audit.put("requestUri", "/ticket/writeOff");
            audit.put("requestParams", "id=" + dto.getId() + ", goType=" + dto.getGoType());
            audit.put("beforeData", JSONObject.toJSONString(beforeData));
            audit.put("afterData", JSONObject.toJSONString(afterData));
            audit.put("userId", UserExt.getUser() == null ? null : UserExt.getUser().getId());
            audit.put("userAccount", UserExt.getUser() == null ? null : UserExt.getUser().getAccount());
            audit.put("result", result);
            audit.put("errorMessage", errorMessage);
            audit.put("createdAt", new Date());
            auditWriter.append(audit);
        } catch (Exception ignored) {
            // 审计不可用时不阻断门票核销,网关审计会记录请求作为兜底。
        }
    }
    /**
     *  购买详情
@@ -407,7 +540,6 @@
    @ResponseBody
    public Object GoodsWriteOff(Integer id){
        System.out.println(id);
        // TODO: 2023/8/15 核销操作
        return new SuccessTip<>();
    }