Commit bdf9e89151890af2bc168f6b74498d7a8169c5eb
1 parent
96009bc9
更新.gitignore以包含.zip文件和Cursor IDE文件,添加WeChatBotService和LqKdKdjlbStringGenerator的依…
…赖注入,改进LqKdKdjlbService以生成并发送开单记录字符串到企业微信群,更新package.json以支持legacy模式。
Showing
75 changed files
with
1436 additions
and
2 deletions
.DS_Store
No preview for this file type
.gitignore
README.md
0 → 100644
| 1 | +# 绿纤ERP管理系统 | |
| 2 | + | |
| 3 | +## 项目简介 | |
| 4 | + | |
| 5 | +绿纤ERP管理系统是一个基于现代化技术栈开发的企业资源规划系统,专为绿纤行业量身定制。系统采用前后端分离架构,提供完整的生产、销售、库存、财务等业务管理功能。 | |
| 6 | + | |
| 7 | +## 技术栈 | |
| 8 | + | |
| 9 | +### 后端技术 | |
| 10 | +- **.NET Core 3.1/5.0** - 跨平台Web框架 | |
| 11 | +- **SqlSugar** - 轻量级ORM框架 | |
| 12 | +- **MySQL** - 关系型数据库 | |
| 13 | +- **JWT** - 身份认证 | |
| 14 | +- **Serilog** - 结构化日志 | |
| 15 | +- **Swagger** - API文档 | |
| 16 | + | |
| 17 | +### 前端技术 | |
| 18 | +- **Vue 2.6** - 渐进式JavaScript框架 | |
| 19 | +- **Element UI** - 企业级UI组件库 | |
| 20 | +- **Vuex** - 状态管理 | |
| 21 | +- **Vue Router** - 路由管理 | |
| 22 | +- **Axios** - HTTP客户端 | |
| 23 | +- **SCSS** - CSS预处理器 | |
| 24 | + | |
| 25 | +## 项目结构 | |
| 26 | + | |
| 27 | +``` | |
| 28 | +lvqianmeiye_ERP/ | |
| 29 | +├── netcore/ # 后端.NET Core项目 | |
| 30 | +│ └── src/ | |
| 31 | +│ ├── Application/ # 应用层 | |
| 32 | +│ ├── Infrastructure/ # 基础设施层 | |
| 33 | +│ └── Modularity/ # 业务模块层 | |
| 34 | +│ ├── System/ # 系统管理模块 | |
| 35 | +│ ├── Extend/ # 🎯 核心业务模块(绿纤ERP业务逻辑) | |
| 36 | +│ │ ├── NCC.Extend/ # 业务服务层 | |
| 37 | +│ │ ├── NCC.Extend.Entitys/ # 实体模型层 | |
| 38 | +│ │ └── NCC.Extend.Interfaces/ # 接口定义层 | |
| 39 | +│ ├── OAuth/ # 身份认证模块 | |
| 40 | +│ ├── Message/ # 消息中心模块 | |
| 41 | +│ ├── Order/ # 订单管理模块 | |
| 42 | +│ ├── VisualDev/ # 可视化开发模块 | |
| 43 | +│ └── Tenant/ # 多租户支持模块 | |
| 44 | +├── antis-ncc-admin/ # 前端Vue项目 | |
| 45 | +│ ├── src/ | |
| 46 | +│ │ ├── api/ # API接口 | |
| 47 | +│ │ ├── components/ # 公共组件 | |
| 48 | +│ │ ├── views/ # 页面视图 | |
| 49 | +│ │ │ ├── lqBmzb/ # 部门管理页面 | |
| 50 | +│ │ │ ├── lqCpxx/ # 产品信息页面 | |
| 51 | +│ │ │ ├── lqHygl/ # 会员管理页面 | |
| 52 | +│ │ │ └── ... # 其他业务页面 | |
| 53 | +│ │ ├── router/ # 路由配置 | |
| 54 | +│ │ └── store/ # 状态管理 | |
| 55 | +│ ├── package.json # 前端依赖配置 | |
| 56 | +│ └── vue.config.js # Vue构建配置 | |
| 57 | +└── html/ # 静态资源 | |
| 58 | +``` | |
| 59 | + | |
| 60 | +## 🎯 核心业务模块说明 | |
| 61 | + | |
| 62 | +**Extend模块**是系统的核心业务模块,包含绿纤ERP的所有业务逻辑。该模块采用三层架构设计: | |
| 63 | + | |
| 64 | +### NCC.Extend(业务服务层) | |
| 65 | +包含所有业务服务的具体实现,按功能分类: | |
| 66 | + | |
| 67 | +#### 📋 基础管理类 | |
| 68 | +- `LqBmzbService` - 部门管理服务 | |
| 69 | +- `LqCpxxService` - 产品信息管理服务 | |
| 70 | +- `LqHyglService` - 会员管理服务 | |
| 71 | +- `LqKhxxService` - 客户信息管理服务 | |
| 72 | +- `LqRyzlService` - 人员资料管理服务 | |
| 73 | + | |
| 74 | +#### 🏭 生产管理类 | |
| 75 | +- `LqJsfaService` - 工艺方案管理服务 | |
| 76 | +- `LqSbtjbService` - 设备统计管理服务 | |
| 77 | +- `LqYcsdJsjService` - 技术数据管理服务 | |
| 78 | +- `LqYcsdJjlftjsService` - 技术方案管理服务 | |
| 79 | +- `LqYcsdMdlbjhsxxService` - 模具管理服务 | |
| 80 | + | |
| 81 | +#### 💰 销售管理类 | |
| 82 | +- `LqXhHyhkService` - 销售回款管理服务 | |
| 83 | +- `LqXhmxbService` - 销售明细管理服务 | |
| 84 | +- `LqTkXscService` - 提成管理服务 | |
| 85 | +- `LqTkjlbService` - 提成记录管理服务 | |
| 86 | + | |
| 87 | +#### 📦 库存管理类 | |
| 88 | +- `LqKqhzbService` - 库存汇总管理服务 | |
| 89 | +- `LqLssjService` - 历史数据管理服务 | |
| 90 | +- `LqYjmxbService` - 原料明细管理服务 | |
| 91 | + | |
| 92 | +#### 📊 财务管理类 | |
| 93 | +- `LqSkzhService` - 收款账户管理服务 | |
| 94 | +- `LqHzfService` - 汇总费用管理服务 | |
| 95 | +- `LqJdqdService` - 季度清单管理服务 | |
| 96 | + | |
| 97 | +#### 🔧 系统工具类 | |
| 98 | +- `EmailService` - 邮件服务 | |
| 99 | +- `DocumentService` - 文档管理服务 | |
| 100 | +- `BigDataService` - 大数据分析服务 | |
| 101 | +- `WorkLogService` - 工作日志服务 | |
| 102 | + | |
| 103 | +### NCC.Extend.Entitys(实体模型层) | |
| 104 | +- **Entity/** - 数据库实体模型(66个实体) | |
| 105 | +- **Dto/** - 数据传输对象(304个DTO) | |
| 106 | +- **Mapper/** - 对象映射配置(50个映射器) | |
| 107 | +- **Model/** - 业务模型类 | |
| 108 | + | |
| 109 | +### NCC.Extend.Interfaces(接口定义层) | |
| 110 | +- 定义所有业务服务的接口契约 | |
| 111 | +- 支持依赖注入和单元测试 | |
| 112 | +- 提供清晰的业务边界 | |
| 113 | + | |
| 114 | +## 环境要求 | |
| 115 | + | |
| 116 | +### 开发环境 | |
| 117 | +- **Node.js**: 16.20.2 (必须使用此版本) | |
| 118 | +- **.NET Core SDK**: 3.1 或 5.0 | |
| 119 | +- **MySQL**: 5.7 或 8.0 | |
| 120 | +- **Visual Studio 2019/2022** 或 **VS Code** | |
| 121 | + | |
| 122 | +### 浏览器支持 | |
| 123 | +- Chrome 70+ | |
| 124 | +- Firefox 65+ | |
| 125 | +- Safari 12+ | |
| 126 | +- Edge 79+ | |
| 127 | + | |
| 128 | +## 快速开始 | |
| 129 | + | |
| 130 | +### 1. 克隆项目 | |
| 131 | +```bash | |
| 132 | +git clone [项目地址] | |
| 133 | +cd lvqianmeiye_ERP | |
| 134 | +``` | |
| 135 | + | |
| 136 | +### 2. 后端启动 | |
| 137 | + | |
| 138 | +#### 方式一:Visual Studio | |
| 139 | +1. 打开 `netcore/smart.agriculture.platform.NET.sln` | |
| 140 | +2. 设置 `NCC.API` 为启动项目 | |
| 141 | +3. 按 F5 运行 | |
| 142 | + | |
| 143 | +#### 方式二:命令行 | |
| 144 | +```bash | |
| 145 | +cd netcore/src/Application/NCC.API | |
| 146 | +dotnet restore | |
| 147 | +dotnet run | |
| 148 | +``` | |
| 149 | + | |
| 150 | +### 3. 前端启动 | |
| 151 | + | |
| 152 | +```bash | |
| 153 | +cd antis-ncc-admin | |
| 154 | + | |
| 155 | +# 安装依赖 | |
| 156 | +npm install | |
| 157 | + | |
| 158 | +# 启动开发服务器 | |
| 159 | +npm run dev | |
| 160 | +``` | |
| 161 | + | |
| 162 | +### 4. 访问系统 | |
| 163 | +- 前端地址:http://localhost:3000 | |
| 164 | +- 后端API:http://localhost:5000 | |
| 165 | +- API文档:http://localhost:5000/antis.doc | |
| 166 | + | |
| 167 | +## 默认账号 | |
| 168 | + | |
| 169 | +- **管理员账号**:admin | |
| 170 | +- **默认密码**:123456 | |
| 171 | + | |
| 172 | +## 主要功能模块 | |
| 173 | + | |
| 174 | +> 💡 **说明**:所有业务功能的核心代码都位于 `netcore/src/Modularity/Extend/` 模块中 | |
| 175 | + | |
| 176 | +### 📋 基础管理模块 | |
| 177 | +- **部门管理** (`LqBmzbService`) - 组织架构管理,部门分类维护 | |
| 178 | +- **产品信息** (`LqCpxxService`) - 产品档案管理,产品分类维护 | |
| 179 | +- **会员管理** (`LqHyglService`) - 客户信息管理,会员等级维护 | |
| 180 | +- **客户信息** (`LqKhxxService`) - 客户档案管理,客户关系维护 | |
| 181 | +- **人员资料** (`LqRyzlService`) - 员工信息管理,人员档案维护 | |
| 182 | + | |
| 183 | +### 🏭 生产管理模块 | |
| 184 | +- **工艺方案** (`LqJsfaService`) - 生产工艺配置,工艺流程管理 | |
| 185 | +- **设备统计** (`LqSbtjbService`) - 设备使用情况统计,设备维护管理 | |
| 186 | +- **技术数据** (`LqYcsdJsjService`) - 技术参数管理,工艺数据维护 | |
| 187 | +- **技术方案** (`LqYcsdJjlftjsService`) - 技术方案管理,工艺优化 | |
| 188 | +- **模具管理** (`LqYcsdMdlbjhsxxService`) - 模具档案管理,模具维护 | |
| 189 | + | |
| 190 | +### 💰 销售管理模块 | |
| 191 | +- **销售回款** (`LqXhHyhkService`) - 销售回款管理,资金回笼跟踪 | |
| 192 | +- **销售明细** (`LqXhmxbService`) - 销售明细管理,销售数据分析 | |
| 193 | +- **提成管理** (`LqTkXscService`) - 销售提成计算,提成规则维护 | |
| 194 | +- **提成记录** (`LqTkjlbService`) - 提成记录管理,提成发放跟踪 | |
| 195 | + | |
| 196 | +### 📦 库存管理模块 | |
| 197 | +- **库存汇总** (`LqKqhzbService`) - 库存数据分析,库存统计报表 | |
| 198 | +- **历史数据** (`LqLssjService`) - 历史数据管理,数据归档 | |
| 199 | +- **原料明细** (`LqYjmxbService`) - 原料库存管理,原料使用跟踪 | |
| 200 | + | |
| 201 | +### 📊 财务管理模块 | |
| 202 | +- **收款账户** (`LqSkzhService`) - 收款账户管理,资金账户维护 | |
| 203 | +- **汇总费用** (`LqHzfService`) - 费用汇总管理,成本分析 | |
| 204 | +- **季度清单** (`LqJdqdService`) - 季度财务清单,财务报告 | |
| 205 | + | |
| 206 | +### 🔧 系统工具模块 | |
| 207 | +- **邮件服务** (`EmailService`) - 邮件发送,通知提醒 | |
| 208 | +- **文档管理** (`DocumentService`) - 文档存储,文件管理 | |
| 209 | +- **大数据分析** (`BigDataService`) - 数据分析,报表生成 | |
| 210 | +- **工作日志** (`WorkLogService`) - 工作日志记录,操作跟踪 | |
| 211 | + | |
| 212 | +### ⚙️ 系统管理模块(位于System模块) | |
| 213 | +- **用户管理** - 系统用户维护,用户权限管理 | |
| 214 | +- **角色权限** - 权限分配管理,角色权限配置 | |
| 215 | +- **系统配置** - 系统参数设置,基础配置维护 | |
| 216 | +- **日志管理** - 操作日志查看,系统日志分析 | |
| 217 | + | |
| 218 | +## 开发指南 | |
| 219 | + | |
| 220 | +### 🎯 核心业务开发(Extend模块) | |
| 221 | + | |
| 222 | +#### 添加新的业务服务 | |
| 223 | +1. **创建实体模型** (`NCC.Extend.Entitys/Entity/`) | |
| 224 | + ```csharp | |
| 225 | + [SugarTable("lq_新表名")] | |
| 226 | + [Tenant(ClaimConst.TENANT_ID)] | |
| 227 | + public class Lq新实体Entity | |
| 228 | + { | |
| 229 | + [SugarColumn(ColumnName = "F_Id", IsPrimaryKey = true)] | |
| 230 | + public string Id { get; set; } | |
| 231 | + // 其他字段... | |
| 232 | + } | |
| 233 | + ``` | |
| 234 | + | |
| 235 | +2. **创建DTO对象** (`NCC.Extend.Entitys/Dto/`) | |
| 236 | + - `Lq新实体ListQueryInput` - 列表查询输入 | |
| 237 | + - `Lq新实体CrInput` - 创建输入 | |
| 238 | + - `Lq新实体UpInput` - 更新输入 | |
| 239 | + - `Lq新实体InfoOutput` - 详情输出 | |
| 240 | + - `Lq新实体ListOutput` - 列表输出 | |
| 241 | + | |
| 242 | +3. **创建接口定义** (`NCC.Extend.Interfaces/`) | |
| 243 | + ```csharp | |
| 244 | + public interface ILq新实体Service : ITransient | |
| 245 | + { | |
| 246 | + Task<dynamic> GetInfo(string id); | |
| 247 | + Task<dynamic> GetList(Lq新实体ListQueryInput input); | |
| 248 | + Task Create(Lq新实体CrInput input); | |
| 249 | + Task Update(string id, Lq新实体UpInput input); | |
| 250 | + Task Delete(string id); | |
| 251 | + } | |
| 252 | + ``` | |
| 253 | + | |
| 254 | +4. **实现业务服务** (`NCC.Extend/`) | |
| 255 | + ```csharp | |
| 256 | + [ApiDescriptionSettings(Tag = "Extend", Name = "Lq新实体", Order = 200)] | |
| 257 | + [Route("api/Extend/[controller]")] | |
| 258 | + public class Lq新实体Service : ILq新实体Service, IDynamicApiController, ITransient | |
| 259 | + { | |
| 260 | + // 服务实现... | |
| 261 | + } | |
| 262 | + ``` | |
| 263 | + | |
| 264 | +#### 前端页面开发 | |
| 265 | +1. **创建页面组件** (`antis-ncc-admin/src/views/lq新模块/`) | |
| 266 | + - `index.vue` - 列表页面 | |
| 267 | + - `Form.vue` - 表单页面 | |
| 268 | + - `ExportBox.vue` - 导出组件 | |
| 269 | + | |
| 270 | +2. **添加API接口** (`antis-ncc-admin/src/api/extend/`) | |
| 271 | + ```javascript | |
| 272 | + export function getLq新实体List(data) { | |
| 273 | + return request({ | |
| 274 | + url: '/api/Extend/Lq新实体', | |
| 275 | + method: 'GET', | |
| 276 | + data | |
| 277 | + }) | |
| 278 | + } | |
| 279 | + ``` | |
| 280 | + | |
| 281 | +### 前端开发 | |
| 282 | +```bash | |
| 283 | +# 开发模式 | |
| 284 | +npm run dev | |
| 285 | + | |
| 286 | +# 构建生产版本 | |
| 287 | +npm run build | |
| 288 | + | |
| 289 | +# 代码检查 | |
| 290 | +npm run lint | |
| 291 | +``` | |
| 292 | + | |
| 293 | +### 后端开发 | |
| 294 | +```bash | |
| 295 | +# 还原包 | |
| 296 | +dotnet restore | |
| 297 | + | |
| 298 | +# 编译项目 | |
| 299 | +dotnet build | |
| 300 | + | |
| 301 | +# 运行项目 | |
| 302 | +dotnet run | |
| 303 | + | |
| 304 | +# 发布项目 | |
| 305 | +dotnet publish -c Release | |
| 306 | +``` | |
| 307 | + | |
| 308 | +## 配置说明 | |
| 309 | + | |
| 310 | +### 数据库配置 | |
| 311 | +修改 `netcore/src/Application/NCC.API/appsettings.json` 中的连接字符串: | |
| 312 | + | |
| 313 | +```json | |
| 314 | +{ | |
| 315 | + "ConnectionStrings": { | |
| 316 | + "DefaultConnection": "Database=lqerp;Data Source=localhost;Port=3306;User Id=root;Password=yourpassword;Charset=utf8;" | |
| 317 | + } | |
| 318 | +} | |
| 319 | +``` | |
| 320 | + | |
| 321 | +### 前端代理配置 | |
| 322 | +修改 `antis-ncc-admin/vue.config.js` 中的代理地址: | |
| 323 | + | |
| 324 | +```javascript | |
| 325 | +proxy: { | |
| 326 | + '/dev': { | |
| 327 | + target: 'http://localhost:5000', // 后端API地址 | |
| 328 | + changeOrigin: true, | |
| 329 | + pathRewrite: { | |
| 330 | + '^/dev': '' | |
| 331 | + } | |
| 332 | + } | |
| 333 | +} | |
| 334 | +``` | |
| 335 | + | |
| 336 | +## 部署说明 | |
| 337 | + | |
| 338 | +### 后端部署 | |
| 339 | +1. 发布项目:`dotnet publish -c Release` | |
| 340 | +2. 配置数据库连接字符串 | |
| 341 | +3. 部署到IIS或Linux服务器 | |
| 342 | + | |
| 343 | +### 前端部署 | |
| 344 | +1. 构建项目:`npm run build` | |
| 345 | +2. 将 `dist` 目录部署到Web服务器 | |
| 346 | +3. 配置Nginx或IIS反向代理 | |
| 347 | + | |
| 348 | +## 常见问题 | |
| 349 | + | |
| 350 | +### Q: 前端启动失败 | |
| 351 | +A: 确保使用Node.js 16.20.2版本,其他版本可能不兼容 | |
| 352 | + | |
| 353 | +### Q: 后端连接数据库失败 | |
| 354 | +A: 检查数据库连接字符串和MySQL服务状态 | |
| 355 | + | |
| 356 | +### Q: 权限验证失败 | |
| 357 | +A: 检查JWT配置和Token有效期设置 | |
| 358 | + | |
| 359 | +### Q: 页面显示异常 | |
| 360 | +A: 检查浏览器控制台错误信息和网络请求状态 | |
| 361 | + | |
| 362 | +## 技术支持 | |
| 363 | + | |
| 364 | +如有技术问题,请联系开发团队或查看项目文档。 | |
| 365 | + | |
| 366 | +## 版本信息 | |
| 367 | + | |
| 368 | +- **当前版本**:v1.0.0 | |
| 369 | +- **最后更新**:2024年 | |
| 370 | +- **维护状态**:活跃开发中 | |
| 371 | + | |
| 372 | +--- | |
| 373 | + | |
| 374 | +**注意**:本项目仅供学习和内部使用,请勿用于商业用途。 | ... | ... |
analyze_excel.py
0 → 100644
| 1 | +#!/usr/bin/env python3 | |
| 2 | +# -*- coding: utf-8 -*- | |
| 3 | +""" | |
| 4 | +Excel工资核算文件分析工具 | |
| 5 | +分析工资核算相关的Excel文件,提取字段信息和引用关系 | |
| 6 | +""" | |
| 7 | + | |
| 8 | +import pandas as pd | |
| 9 | +import os | |
| 10 | +import re | |
| 11 | +from pathlib import Path | |
| 12 | + | |
| 13 | +def analyze_excel_file(file_path, sheet_name=None): | |
| 14 | + """分析单个Excel文件""" | |
| 15 | + try: | |
| 16 | + # 获取所有工作表名称 | |
| 17 | + excel_file = pd.ExcelFile(file_path) | |
| 18 | + sheets = excel_file.sheet_names | |
| 19 | + | |
| 20 | + print(f"\n=== 分析文件: {os.path.basename(file_path)} ===") | |
| 21 | + print(f"工作表数量: {len(sheets)}") | |
| 22 | + print(f"工作表名称: {sheets}") | |
| 23 | + | |
| 24 | + analysis_result = { | |
| 25 | + 'file_name': os.path.basename(file_path), | |
| 26 | + 'sheets': {}, | |
| 27 | + 'all_columns': set(), | |
| 28 | + 'formulas': [], | |
| 29 | + 'references': [] | |
| 30 | + } | |
| 31 | + | |
| 32 | + # 分析每个工作表 | |
| 33 | + for sheet in sheets: | |
| 34 | + if sheet_name and sheet != sheet_name: | |
| 35 | + continue | |
| 36 | + | |
| 37 | + try: | |
| 38 | + df = pd.read_excel(file_path, sheet_name=sheet, header=0) | |
| 39 | + print(f"\n--- 工作表: {sheet} ---") | |
| 40 | + print(f"行数: {len(df)}") | |
| 41 | + print(f"列数: {len(df.columns)}") | |
| 42 | + print(f"列名: {list(df.columns)}") | |
| 43 | + | |
| 44 | + # 收集列名 | |
| 45 | + analysis_result['all_columns'].update(df.columns) | |
| 46 | + analysis_result['sheets'][sheet] = { | |
| 47 | + 'rows': len(df), | |
| 48 | + 'columns': len(df.columns), | |
| 49 | + 'column_names': list(df.columns), | |
| 50 | + 'sample_data': df.head(3).to_dict('records') if len(df) > 0 else [] | |
| 51 | + } | |
| 52 | + | |
| 53 | + # 查找公式和引用 | |
| 54 | + for col in df.columns: | |
| 55 | + for idx, value in df[col].items(): | |
| 56 | + if pd.notna(value) and isinstance(value, str): | |
| 57 | + # 查找Excel公式模式 | |
| 58 | + formula_patterns = [ | |
| 59 | + r'=([A-Z]+[0-9]+)', # 单元格引用 | |
| 60 | + r'=([A-Z]+[0-9]+:[A-Z]+[0-9]+)', # 范围引用 | |
| 61 | + r'=([A-Z]+[0-9]+\[[^\]]+\])', # 数组引用 | |
| 62 | + r'=([A-Z]+[0-9]+\$[0-9]+)', # 绝对引用 | |
| 63 | + r'=([A-Z]+[0-9]+:[A-Z]+[0-9]+\$[0-9]+)', # 绝对范围引用 | |
| 64 | + ] | |
| 65 | + | |
| 66 | + for pattern in formula_patterns: | |
| 67 | + matches = re.findall(pattern, str(value)) | |
| 68 | + if matches: | |
| 69 | + analysis_result['formulas'].append({ | |
| 70 | + 'sheet': sheet, | |
| 71 | + 'cell': f"{col}{idx+1}", | |
| 72 | + 'formula': str(value), | |
| 73 | + 'references': matches | |
| 74 | + }) | |
| 75 | + analysis_result['references'].extend(matches) | |
| 76 | + | |
| 77 | + except Exception as e: | |
| 78 | + print(f"分析工作表 {sheet} 时出错: {e}") | |
| 79 | + analysis_result['sheets'][sheet] = {'error': str(e)} | |
| 80 | + | |
| 81 | + return analysis_result | |
| 82 | + | |
| 83 | + except Exception as e: | |
| 84 | + print(f"分析文件 {file_path} 时出错: {e}") | |
| 85 | + return None | |
| 86 | + | |
| 87 | +def main(): | |
| 88 | + """主函数""" | |
| 89 | + base_path = "/Users/mr.wang/代码库/绿纤/lvqianmeiye_ERP/参考资料/工资核算 -7月" | |
| 90 | + | |
| 91 | + # 要分析的文件列表 | |
| 92 | + files_to_analyze = [ | |
| 93 | + "工资(全字段).xlsx", | |
| 94 | + "①B-a-26考勤汇总表.xlsx", | |
| 95 | + "②B-a-17每日早报.xlsx", | |
| 96 | + "③B-a-③呈现-消耗明细表.xlsx", | |
| 97 | + "④B-a-25社保统计表.xlsx", | |
| 98 | + "⑤B-a-12奖励统计表.xlsx", | |
| 99 | + "B-b-③健康师底薪.xlsx", | |
| 100 | + "B-b-④健康师提成-金三角顾问.xlsx", | |
| 101 | + "B-b-⑤其他岗位工资.xlsx", | |
| 102 | + "B-b-⑤当月数据及门店毛利.xlsx" | |
| 103 | + ] | |
| 104 | + | |
| 105 | + all_results = {} | |
| 106 | + | |
| 107 | + print("开始分析工资核算Excel文件...") | |
| 108 | + print("=" * 60) | |
| 109 | + | |
| 110 | + for filename in files_to_analyze: | |
| 111 | + file_path = os.path.join(base_path, filename) | |
| 112 | + if os.path.exists(file_path): | |
| 113 | + result = analyze_excel_file(file_path) | |
| 114 | + if result: | |
| 115 | + all_results[filename] = result | |
| 116 | + else: | |
| 117 | + print(f"文件不存在: {filename}") | |
| 118 | + | |
| 119 | + # 生成分析报告 | |
| 120 | + generate_report(all_results) | |
| 121 | + | |
| 122 | +def generate_report(all_results): | |
| 123 | + """生成分析报告""" | |
| 124 | + report_path = "/Users/mr.wang/代码库/绿纤/lvqianmeiye_ERP/工资核算Excel分析报告.md" | |
| 125 | + | |
| 126 | + with open(report_path, 'w', encoding='utf-8') as f: | |
| 127 | + f.write("# 工资核算Excel文件分析报告\n\n") | |
| 128 | + f.write("## 概述\n\n") | |
| 129 | + f.write("本报告分析了工资核算相关的Excel文件,提取了字段信息、公式引用关系等关键数据。\n\n") | |
| 130 | + | |
| 131 | + # 文件概览 | |
| 132 | + f.write("## 文件概览\n\n") | |
| 133 | + f.write("| 文件名 | 工作表数量 | 总列数 | 状态 |\n") | |
| 134 | + f.write("|--------|------------|--------|------|\n") | |
| 135 | + | |
| 136 | + for filename, result in all_results.items(): | |
| 137 | + if result: | |
| 138 | + sheet_count = len(result['sheets']) | |
| 139 | + total_columns = len(result['all_columns']) | |
| 140 | + f.write(f"| {filename} | {sheet_count} | {total_columns} | ✅ |\n") | |
| 141 | + else: | |
| 142 | + f.write(f"| {filename} | - | - | ❌ |\n") | |
| 143 | + | |
| 144 | + # 详细分析 | |
| 145 | + f.write("\n## 详细分析\n\n") | |
| 146 | + | |
| 147 | + for filename, result in all_results.items(): | |
| 148 | + if not result: | |
| 149 | + continue | |
| 150 | + | |
| 151 | + f.write(f"### {filename}\n\n") | |
| 152 | + | |
| 153 | + # 工作表信息 | |
| 154 | + f.write("#### 工作表信息\n\n") | |
| 155 | + for sheet_name, sheet_info in result['sheets'].items(): | |
| 156 | + if 'error' in sheet_info: | |
| 157 | + f.write(f"- **{sheet_name}**: 分析出错 - {sheet_info['error']}\n") | |
| 158 | + else: | |
| 159 | + f.write(f"- **{sheet_name}**: {sheet_info['rows']}行 x {sheet_info['columns']}列\n") | |
| 160 | + f.write(f" - 列名: {', '.join([str(col) for col in sheet_info['column_names']])}\n") | |
| 161 | + | |
| 162 | + # 字段统计 | |
| 163 | + f.write(f"\n#### 字段统计\n\n") | |
| 164 | + f.write(f"总字段数: {len(result['all_columns'])}\n\n") | |
| 165 | + f.write("所有字段列表:\n") | |
| 166 | + for col in sorted(result['all_columns'], key=str): | |
| 167 | + f.write(f"- {col}\n") | |
| 168 | + | |
| 169 | + # 公式引用 | |
| 170 | + if result['formulas']: | |
| 171 | + f.write(f"\n#### 公式引用\n\n") | |
| 172 | + f.write(f"发现 {len(result['formulas'])} 个公式引用:\n\n") | |
| 173 | + for formula in result['formulas'][:10]: # 只显示前10个 | |
| 174 | + f.write(f"- **{formula['cell']}** ({formula['sheet']}): {formula['formula']}\n") | |
| 175 | + f.write(f" - 引用: {', '.join(formula['references'])}\n") | |
| 176 | + | |
| 177 | + if len(result['formulas']) > 10: | |
| 178 | + f.write(f"\n... 还有 {len(result['formulas']) - 10} 个公式\n") | |
| 179 | + | |
| 180 | + f.write("\n---\n\n") | |
| 181 | + | |
| 182 | + # 字段汇总 | |
| 183 | + f.write("## 字段汇总\n\n") | |
| 184 | + all_columns = set() | |
| 185 | + for result in all_results.values(): | |
| 186 | + if result: | |
| 187 | + all_columns.update(result['all_columns']) | |
| 188 | + | |
| 189 | + f.write(f"所有文件共发现 {len(all_columns)} 个唯一字段:\n\n") | |
| 190 | + for col in sorted(all_columns, key=str): | |
| 191 | + f.write(f"- {col}\n") | |
| 192 | + | |
| 193 | + # 引用关系汇总 | |
| 194 | + f.write("\n## 引用关系汇总\n\n") | |
| 195 | + all_references = set() | |
| 196 | + for result in all_results.values(): | |
| 197 | + if result: | |
| 198 | + all_references.update(result['references']) | |
| 199 | + | |
| 200 | + f.write(f"发现 {len(all_references)} 个唯一引用:\n\n") | |
| 201 | + for ref in sorted(all_references, key=str): | |
| 202 | + f.write(f"- {ref}\n") | |
| 203 | + | |
| 204 | + print(f"\n分析报告已生成: {report_path}") | |
| 205 | + | |
| 206 | +if __name__ == "__main__": | |
| 207 | + main() | ... | ... |
analyze_formulas.py
0 → 100644
| 1 | +#!/usr/bin/env python3 | |
| 2 | +# -*- coding: utf-8 -*- | |
| 3 | +""" | |
| 4 | +Excel公式分析工具 | |
| 5 | +分析工资核算Excel文件中的公式和引用关系 | |
| 6 | +""" | |
| 7 | + | |
| 8 | +import pandas as pd | |
| 9 | +import openpyxl | |
| 10 | +import os | |
| 11 | +import re | |
| 12 | +from pathlib import Path | |
| 13 | + | |
| 14 | +def extract_formulas_from_excel(file_path): | |
| 15 | + """从Excel文件中提取所有公式""" | |
| 16 | + try: | |
| 17 | + # 使用openpyxl读取Excel文件以获取公式 | |
| 18 | + workbook = openpyxl.load_workbook(file_path, data_only=False) | |
| 19 | + | |
| 20 | + formulas = [] | |
| 21 | + references = [] | |
| 22 | + | |
| 23 | + for sheet_name in workbook.sheetnames: | |
| 24 | + worksheet = workbook[sheet_name] | |
| 25 | + | |
| 26 | + for row in worksheet.iter_rows(): | |
| 27 | + for cell in row: | |
| 28 | + if cell.data_type == 'f': # 公式类型 | |
| 29 | + formula = str(cell.value) | |
| 30 | + cell_ref = f"{sheet_name}!{cell.coordinate}" | |
| 31 | + | |
| 32 | + # 提取公式中的引用 | |
| 33 | + refs = extract_references_from_formula(formula) | |
| 34 | + | |
| 35 | + formulas.append({ | |
| 36 | + 'file': os.path.basename(file_path), | |
| 37 | + 'sheet': sheet_name, | |
| 38 | + 'cell': cell.coordinate, | |
| 39 | + 'formula': formula, | |
| 40 | + 'references': refs | |
| 41 | + }) | |
| 42 | + | |
| 43 | + references.extend(refs) | |
| 44 | + | |
| 45 | + return formulas, references | |
| 46 | + | |
| 47 | + except Exception as e: | |
| 48 | + print(f"分析文件 {file_path} 时出错: {e}") | |
| 49 | + return [], [] | |
| 50 | + | |
| 51 | +def extract_references_from_formula(formula): | |
| 52 | + """从公式中提取引用关系""" | |
| 53 | + references = [] | |
| 54 | + | |
| 55 | + # 匹配各种引用模式 | |
| 56 | + patterns = [ | |
| 57 | + # XLOOKUP引用模式 | |
| 58 | + r"XLOOKUP\([^,]+,\s*'([^']+)'!([^,]+),\s*'([^']+)'!([^,]+)", | |
| 59 | + # VLOOKUP引用模式 | |
| 60 | + r"VLOOKUP\([^,]+,\s*'([^']+)'!([^,]+)", | |
| 61 | + # 直接工作表引用 | |
| 62 | + r"'([^']+)'!([A-Z]+\d+)", | |
| 63 | + # 工作表范围引用 | |
| 64 | + r"'([^']+)'!([A-Z]+\d+:[A-Z]+\d+)", | |
| 65 | + # 简单单元格引用 | |
| 66 | + r"([A-Z]+\d+)", | |
| 67 | + # 范围引用 | |
| 68 | + r"([A-Z]+\d+:[A-Z]+\d+)", | |
| 69 | + ] | |
| 70 | + | |
| 71 | + for pattern in patterns: | |
| 72 | + matches = re.findall(pattern, formula) | |
| 73 | + for match in matches: | |
| 74 | + if isinstance(match, tuple): | |
| 75 | + if len(match) == 2: | |
| 76 | + references.append({ | |
| 77 | + 'type': 'worksheet_reference', | |
| 78 | + 'file': match[0] if match[0] else None, | |
| 79 | + 'range': match[1] | |
| 80 | + }) | |
| 81 | + elif len(match) == 4: | |
| 82 | + references.append({ | |
| 83 | + 'type': 'xlookup_reference', | |
| 84 | + 'file': match[0], | |
| 85 | + 'lookup_range': match[1], | |
| 86 | + 'file2': match[2], | |
| 87 | + 'return_range': match[3] | |
| 88 | + }) | |
| 89 | + else: | |
| 90 | + references.append({ | |
| 91 | + 'type': 'cell_reference', | |
| 92 | + 'range': match | |
| 93 | + }) | |
| 94 | + | |
| 95 | + return references | |
| 96 | + | |
| 97 | +def analyze_formula_dependencies(formulas): | |
| 98 | + """分析公式依赖关系""" | |
| 99 | + dependencies = {} | |
| 100 | + | |
| 101 | + for formula in formulas: | |
| 102 | + cell_key = f"{formula['file']}!{formula['sheet']}!{formula['cell']}" | |
| 103 | + dependencies[cell_key] = { | |
| 104 | + 'formula': formula['formula'], | |
| 105 | + 'dependencies': formula['references'] | |
| 106 | + } | |
| 107 | + | |
| 108 | + return dependencies | |
| 109 | + | |
| 110 | +def generate_formula_report(formulas, references, dependencies): | |
| 111 | + """生成公式分析报告""" | |
| 112 | + report_path = "/Users/mr.wang/代码库/绿纤/lvqianmeiye_ERP/Excel公式分析报告.md" | |
| 113 | + | |
| 114 | + with open(report_path, 'w', encoding='utf-8') as f: | |
| 115 | + f.write("# Excel公式分析报告\n\n") | |
| 116 | + f.write("## 概述\n\n") | |
| 117 | + f.write("本报告分析了工资核算Excel文件中的公式和引用关系,帮助理解数据计算逻辑。\n\n") | |
| 118 | + | |
| 119 | + # 统计信息 | |
| 120 | + f.write("## 统计信息\n\n") | |
| 121 | + f.write(f"- 总公式数量: {len(formulas)}\n") | |
| 122 | + f.write(f"- 总引用数量: {len(references)}\n") | |
| 123 | + f.write(f"- 涉及文件数量: {len(set(f['file'] for f in formulas))}\n\n") | |
| 124 | + | |
| 125 | + # 按文件分组分析 | |
| 126 | + files = {} | |
| 127 | + for formula in formulas: | |
| 128 | + file_name = formula['file'] | |
| 129 | + if file_name not in files: | |
| 130 | + files[file_name] = [] | |
| 131 | + files[file_name].append(formula) | |
| 132 | + | |
| 133 | + f.write("## 文件公式分析\n\n") | |
| 134 | + | |
| 135 | + for file_name, file_formulas in files.items(): | |
| 136 | + f.write(f"### {file_name}\n\n") | |
| 137 | + f.write(f"公式数量: {len(file_formulas)}\n\n") | |
| 138 | + | |
| 139 | + # 按工作表分组 | |
| 140 | + sheets = {} | |
| 141 | + for formula in file_formulas: | |
| 142 | + sheet_name = formula['sheet'] | |
| 143 | + if sheet_name not in sheets: | |
| 144 | + sheets[sheet_name] = [] | |
| 145 | + sheets[sheet_name].append(formula) | |
| 146 | + | |
| 147 | + for sheet_name, sheet_formulas in sheets.items(): | |
| 148 | + f.write(f"#### 工作表: {sheet_name}\n\n") | |
| 149 | + f.write(f"公式数量: {len(sheet_formulas)}\n\n") | |
| 150 | + | |
| 151 | + # 显示前10个公式 | |
| 152 | + for i, formula in enumerate(sheet_formulas[:10]): | |
| 153 | + f.write(f"**{formula['cell']}**:\n") | |
| 154 | + f.write(f"```\n{formula['formula']}\n```\n") | |
| 155 | + | |
| 156 | + if formula['references']: | |
| 157 | + f.write("引用关系:\n") | |
| 158 | + for ref in formula['references']: | |
| 159 | + if ref['type'] == 'xlookup_reference': | |
| 160 | + f.write(f"- XLOOKUP: {ref['file']}!{ref['lookup_range']} -> {ref['file2']}!{ref['return_range']}\n") | |
| 161 | + elif ref['type'] == 'worksheet_reference': | |
| 162 | + f.write(f"- 工作表引用: {ref['file']}!{ref['range']}\n") | |
| 163 | + elif ref['type'] == 'cell_reference': | |
| 164 | + f.write(f"- 单元格引用: {ref['range']}\n") | |
| 165 | + f.write("\n") | |
| 166 | + else: | |
| 167 | + f.write("无外部引用\n\n") | |
| 168 | + | |
| 169 | + if len(sheet_formulas) > 10: | |
| 170 | + f.write(f"... 还有 {len(sheet_formulas) - 10} 个公式\n\n") | |
| 171 | + | |
| 172 | + f.write("---\n\n") | |
| 173 | + | |
| 174 | + # 引用关系汇总 | |
| 175 | + f.write("## 引用关系汇总\n\n") | |
| 176 | + | |
| 177 | + # 统计引用类型 | |
| 178 | + ref_types = {} | |
| 179 | + for ref in references: | |
| 180 | + ref_type = ref['type'] | |
| 181 | + if ref_type not in ref_types: | |
| 182 | + ref_types[ref_type] = 0 | |
| 183 | + ref_types[ref_type] += 1 | |
| 184 | + | |
| 185 | + f.write("### 引用类型统计\n\n") | |
| 186 | + for ref_type, count in ref_types.items(): | |
| 187 | + f.write(f"- {ref_type}: {count} 个\n") | |
| 188 | + f.write("\n") | |
| 189 | + | |
| 190 | + # 外部文件引用 | |
| 191 | + external_refs = {} | |
| 192 | + for ref in references: | |
| 193 | + if ref['type'] == 'xlookup_reference' and ref['file']: | |
| 194 | + file_name = ref['file'] | |
| 195 | + if file_name not in external_refs: | |
| 196 | + external_refs[file_name] = [] | |
| 197 | + external_refs[file_name].append(ref) | |
| 198 | + | |
| 199 | + if external_refs: | |
| 200 | + f.write("### 外部文件引用\n\n") | |
| 201 | + for file_name, refs in external_refs.items(): | |
| 202 | + f.write(f"**{file_name}**: {len(refs)} 个引用\n") | |
| 203 | + for ref in refs[:5]: # 只显示前5个 | |
| 204 | + f.write(f"- {ref['file']}!{ref['lookup_range']} -> {ref['file2']}!{ref['return_range']}\n") | |
| 205 | + if len(refs) > 5: | |
| 206 | + f.write(f"... 还有 {len(refs) - 5} 个引用\n") | |
| 207 | + f.write("\n") | |
| 208 | + | |
| 209 | + # 公式依赖关系图 | |
| 210 | + f.write("## 公式依赖关系\n\n") | |
| 211 | + f.write("### 关键公式分析\n\n") | |
| 212 | + | |
| 213 | + # 找出包含XLOOKUP的公式 | |
| 214 | + xlookup_formulas = [f for f in formulas if 'XLOOKUP' in f['formula']] | |
| 215 | + | |
| 216 | + if xlookup_formulas: | |
| 217 | + f.write("#### XLOOKUP公式分析\n\n") | |
| 218 | + for formula in xlookup_formulas[:10]: | |
| 219 | + f.write(f"**{formula['file']}!{formula['sheet']}!{formula['cell']}**:\n") | |
| 220 | + f.write(f"```\n{formula['formula']}\n```\n") | |
| 221 | + | |
| 222 | + # 解析XLOOKUP参数 | |
| 223 | + xlookup_match = re.search(r"XLOOKUP\(([^,]+),\s*'([^']+)'!([^,]+),\s*'([^']+)'!([^,]+)", formula['formula']) | |
| 224 | + if xlookup_match: | |
| 225 | + lookup_value = xlookup_match.group(1).strip() | |
| 226 | + lookup_file = xlookup_match.group(2) | |
| 227 | + lookup_range = xlookup_match.group(3) | |
| 228 | + return_file = xlookup_match.group(4) | |
| 229 | + return_range = xlookup_match.group(5) | |
| 230 | + | |
| 231 | + f.write("参数解析:\n") | |
| 232 | + f.write(f"- 查找值: {lookup_value}\n") | |
| 233 | + f.write(f"- 查找范围: {lookup_file}!{lookup_range}\n") | |
| 234 | + f.write(f"- 返回范围: {return_file}!{return_range}\n") | |
| 235 | + f.write("\n") | |
| 236 | + | |
| 237 | + # 数据流分析 | |
| 238 | + f.write("### 数据流向分析\n\n") | |
| 239 | + f.write("基于公式分析,数据流向如下:\n\n") | |
| 240 | + | |
| 241 | + # 分析数据源文件 | |
| 242 | + data_sources = set() | |
| 243 | + for ref in references: | |
| 244 | + if ref['type'] == 'xlookup_reference' and ref['file']: | |
| 245 | + data_sources.add(ref['file']) | |
| 246 | + | |
| 247 | + f.write("**数据源文件**:\n") | |
| 248 | + for source in sorted(data_sources): | |
| 249 | + f.write(f"- {source}\n") | |
| 250 | + f.write("\n") | |
| 251 | + | |
| 252 | + f.write("**数据流向**:\n") | |
| 253 | + f.write("1. 基础数据从各数据源文件获取\n") | |
| 254 | + f.write("2. 通过XLOOKUP/VLOOKUP函数进行数据匹配\n") | |
| 255 | + f.write("3. 在工资核算表中进行汇总计算\n") | |
| 256 | + f.write("4. 生成最终的工资计算结果\n\n") | |
| 257 | + | |
| 258 | + return report_path | |
| 259 | + | |
| 260 | +def main(): | |
| 261 | + """主函数""" | |
| 262 | + base_path = "/Users/mr.wang/代码库/绿纤/lvqianmeiye_ERP/参考资料/工资核算 -7月" | |
| 263 | + | |
| 264 | + # 要分析的文件列表 | |
| 265 | + files_to_analyze = [ | |
| 266 | + "工资(全字段).xlsx", | |
| 267 | + "①B-a-26考勤汇总表.xlsx", | |
| 268 | + "②B-a-17每日早报.xlsx", | |
| 269 | + "③B-a-③呈现-消耗明细表.xlsx", | |
| 270 | + "④B-a-25社保统计表.xlsx", | |
| 271 | + "⑤B-a-12奖励统计表.xlsx", | |
| 272 | + "B-b-③健康师底薪.xlsx", | |
| 273 | + "B-b-④健康师提成-金三角顾问.xlsx", | |
| 274 | + "B-b-⑤其他岗位工资.xlsx", | |
| 275 | + "B-b-⑤当月数据及门店毛利.xlsx" | |
| 276 | + ] | |
| 277 | + | |
| 278 | + all_formulas = [] | |
| 279 | + all_references = [] | |
| 280 | + | |
| 281 | + print("开始分析Excel公式...") | |
| 282 | + print("=" * 60) | |
| 283 | + | |
| 284 | + for filename in files_to_analyze: | |
| 285 | + file_path = os.path.join(base_path, filename) | |
| 286 | + if os.path.exists(file_path): | |
| 287 | + print(f"正在分析: {filename}") | |
| 288 | + formulas, references = extract_formulas_from_excel(file_path) | |
| 289 | + all_formulas.extend(formulas) | |
| 290 | + all_references.extend(references) | |
| 291 | + print(f" 发现 {len(formulas)} 个公式") | |
| 292 | + else: | |
| 293 | + print(f"文件不存在: {filename}") | |
| 294 | + | |
| 295 | + # 分析依赖关系 | |
| 296 | + dependencies = analyze_formula_dependencies(all_formulas) | |
| 297 | + | |
| 298 | + # 生成报告 | |
| 299 | + report_path = generate_formula_report(all_formulas, all_references, dependencies) | |
| 300 | + | |
| 301 | + print(f"\n公式分析完成!") | |
| 302 | + print(f"总公式数量: {len(all_formulas)}") | |
| 303 | + print(f"总引用数量: {len(all_references)}") | |
| 304 | + print(f"分析报告: {report_path}") | |
| 305 | + | |
| 306 | +if __name__ == "__main__": | |
| 307 | + main() | ... | ... |
antis-ncc-admin/.DS_Store
No preview for this file type
antis-ncc-admin/.nvmrc
0 → 100644
antis-ncc-admin/package-lock.json
| ... | ... | @@ -13634,6 +13634,14 @@ |
| 13634 | 13634 | "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", |
| 13635 | 13635 | "dev": true |
| 13636 | 13636 | }, |
| 13637 | + "node_modules/nan": { | |
| 13638 | + "version": "2.23.0", | |
| 13639 | + "resolved": "https://registry.npmjs.org/nan/-/nan-2.23.0.tgz", | |
| 13640 | + "integrity": "sha512-1UxuyYGdoQHcGg87Lkqm3FzefucTa0NAiOcuRsDmysep3c1LVCRK2krrUDafMWtjSG04htvAmvg96+SDknOmgQ==", | |
| 13641 | + "dev": true, | |
| 13642 | + "license": "MIT", | |
| 13643 | + "optional": true | |
| 13644 | + }, | |
| 13637 | 13645 | "node_modules/nanomatch": { |
| 13638 | 13646 | "version": "1.2.13", |
| 13639 | 13647 | "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", |
| ... | ... | @@ -27809,6 +27817,7 @@ |
| 27809 | 27817 | }, |
| 27810 | 27818 | "eve": { |
| 27811 | 27819 | "version": "git+ssh://git@github.com/adobe-webplatform/eve.git#eef80ed8d188423c2272746fb8ae5cc8dad84cb1", |
| 27820 | + "integrity": "sha512-VrMCMjRWGSuyiV/1SP58K4lIui3jlfD3uBapV8jahx9/RsqZad/FL7Tq5NRTHWFI8yd1dVLgUrEDPrA70gnOTw==", | |
| 27812 | 27821 | "from": "eve@git://github.com/adobe-webplatform/eve.git#eef80ed" |
| 27813 | 27822 | }, |
| 27814 | 27823 | "event-pubsub": { |
| ... | ... | @@ -32943,6 +32952,13 @@ |
| 32943 | 32952 | "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", |
| 32944 | 32953 | "dev": true |
| 32945 | 32954 | }, |
| 32955 | + "nan": { | |
| 32956 | + "version": "2.23.0", | |
| 32957 | + "resolved": "https://registry.npmjs.org/nan/-/nan-2.23.0.tgz", | |
| 32958 | + "integrity": "sha512-1UxuyYGdoQHcGg87Lkqm3FzefucTa0NAiOcuRsDmysep3c1LVCRK2krrUDafMWtjSG04htvAmvg96+SDknOmgQ==", | |
| 32959 | + "dev": true, | |
| 32960 | + "optional": true | |
| 32961 | + }, | |
| 32946 | 32962 | "nanomatch": { |
| 32947 | 32963 | "version": "1.2.13", |
| 32948 | 32964 | "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", |
| ... | ... | @@ -36827,6 +36843,7 @@ |
| 36827 | 36843 | }, |
| 36828 | 36844 | "squire-rte": { |
| 36829 | 36845 | "version": "git+ssh://git@github.com/sohee-lee7/Squire.git#b1e0e1031fa18912d233c204cbe7c7fae4a42621", |
| 36846 | + "integrity": "sha512-/1Wi323QPO3AmcGOXjliEp2Kqzse6/BbCyCj/lw0R0M/zS+7IFm1LnBlgMlfa1yLXRjNcc5PcmE2vnwA4Zlhkg==", | |
| 36830 | 36847 | "from": "squire-rte@github:sohee-lee7/Squire#b1e0e1031fa18912d233c204cbe7c7fae4a42621" |
| 36831 | 36848 | }, |
| 36832 | 36849 | "sshpk": { | ... | ... |
antis-ncc-admin/package.json
| ... | ... | @@ -6,6 +6,7 @@ |
| 6 | 6 | "license": "MIT", |
| 7 | 7 | "scripts": { |
| 8 | 8 | "dev": "vue-cli-service serve --open", |
| 9 | + "dev:legacy": "NODE_OPTIONS=\"--openssl-legacy-provider\" vue-cli-service serve --open", | |
| 9 | 10 | "build": "cross-env NODE_ENV=production vue-cli-service build", |
| 10 | 11 | "build:staging": "cross-env NODE_ENV=production vue-cli-service build --mode staging", |
| 11 | 12 | "build:javaBootDev": "cross-env NODE_ENV=production vue-cli-service build --mode javaBootDev", | ... | ... |
antis-ncc-admin/start-dev.sh
0 → 100755
| 1 | +#!/bin/bash | |
| 2 | + | |
| 3 | +# 绿纤ERP前端启动脚本 | |
| 4 | +echo "🚀 启动绿纤ERP前端项目..." | |
| 5 | + | |
| 6 | +# 检查nvm是否安装 | |
| 7 | +if ! command -v nvm &> /dev/null; then | |
| 8 | + echo "❌ nvm未安装,请先安装nvm" | |
| 9 | + exit 1 | |
| 10 | +fi | |
| 11 | + | |
| 12 | +# 切换到项目目录 | |
| 13 | +cd "$(dirname "$0")" | |
| 14 | + | |
| 15 | +# 使用nvm切换到指定Node.js版本 | |
| 16 | +echo "📦 切换到Node.js 16.20.2..." | |
| 17 | +nvm use | |
| 18 | + | |
| 19 | +# 检查Node.js版本 | |
| 20 | +echo "✅ 当前Node.js版本: $(node --version)" | |
| 21 | + | |
| 22 | +# 安装依赖(如果需要) | |
| 23 | +if [ ! -d "node_modules" ]; then | |
| 24 | + echo "📥 安装项目依赖..." | |
| 25 | + npm install | |
| 26 | +fi | |
| 27 | + | |
| 28 | +# 启动开发服务器 | |
| 29 | +echo "🌐 启动前端开发服务器..." | |
| 30 | +echo "📍 访问地址: http://localhost:3000" | |
| 31 | +echo "🛑 按 Ctrl+C 停止服务" | |
| 32 | +echo "" | |
| 33 | + | |
| 34 | +npm run dev | |
| 35 | + | ... | ... |
netcore/.DS_Store
No preview for this file type
netcore/src/.DS_Store
No preview for this file type
netcore/src/Application/.DS_Store
No preview for this file type
netcore/src/Application/NCC.API.Core/Startup.cs
| ... | ... | @@ -30,6 +30,7 @@ using Antis.Pay.Core.Interface; |
| 30 | 30 | using Antis.Pay.Core; |
| 31 | 31 | using Microsoft.AspNetCore.Http; |
| 32 | 32 | using Microsoft.AspNetCore.Http.Features; |
| 33 | +using NCC.Extend.Utils; | |
| 33 | 34 | |
| 34 | 35 | namespace NCC.API.Core |
| 35 | 36 | { |
| ... | ... | @@ -58,6 +59,12 @@ namespace NCC.API.Core |
| 58 | 59 | services.AddConfigurableOptions<CacheOptions>(); |
| 59 | 60 | //使用微信支付实现 |
| 60 | 61 | services.AddScoped<IWePay, WePay>(); |
| 62 | + | |
| 63 | + // 注册企业微信机器人服务 | |
| 64 | + services.AddHttpClient<WeChatBotService>(); | |
| 65 | + | |
| 66 | + // 注册开单记录字符串生成器 | |
| 67 | + services.AddScoped<LqKdKdjlbStringGenerator>(); | |
| 61 | 68 | |
| 62 | 69 | services.AddControllersWithViews() |
| 63 | 70 | .AddMvcFilter<RequestActionFilter>() | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/LqKdKdjlbService.cs
| ... | ... | @@ -22,6 +22,8 @@ using NCC.Common.Model.NPOI; |
| 22 | 22 | using NCC.Common.Configuration; |
| 23 | 23 | using NCC.DataEncryption; |
| 24 | 24 | using NCC.ClayObject; |
| 25 | +using NCC.Extend.Utils; | |
| 26 | +using System.Net.Http; | |
| 25 | 27 | |
| 26 | 28 | namespace NCC.Extend.LqKdKdjlb |
| 27 | 29 | { |
| ... | ... | @@ -38,6 +40,8 @@ namespace NCC.Extend.LqKdKdjlb |
| 38 | 40 | private readonly ISqlSugarRepository<LqKdPxmxEntity> _lqKdPxmxRepository; |
| 39 | 41 | private readonly SqlSugarScope _db; |
| 40 | 42 | private readonly IUserManager _userManager; |
| 43 | + private readonly WeChatBotService _weChatBotService; | |
| 44 | + private readonly LqKdKdjlbStringGenerator _stringGenerator; | |
| 41 | 45 | |
| 42 | 46 | /// <summary> |
| 43 | 47 | /// 初始化一个<see cref="LqKdKdjlbService"/>类型的新实例 |
| ... | ... | @@ -47,7 +51,9 @@ namespace NCC.Extend.LqKdKdjlb |
| 47 | 51 | ISqlSugarRepository<LqKdJksyjEntity> lqKdJksyjRepository, |
| 48 | 52 | ISqlSugarRepository<LqKdKjbsyjEntity> lqKdKjbsyjRepository, |
| 49 | 53 | ISqlSugarRepository<LqKdPxmxEntity> lqKdPxmxRepository, |
| 50 | - IUserManager userManager) | |
| 54 | + IUserManager userManager, | |
| 55 | + WeChatBotService weChatBotService, | |
| 56 | + LqKdKdjlbStringGenerator stringGenerator) | |
| 51 | 57 | { |
| 52 | 58 | _lqKdKdjlbRepository = lqKdKdjlbRepository; |
| 53 | 59 | _db = _lqKdKdjlbRepository.Context; |
| ... | ... | @@ -55,6 +61,8 @@ namespace NCC.Extend.LqKdKdjlb |
| 55 | 61 | _lqKdKjbsyjRepository = lqKdKjbsyjRepository; |
| 56 | 62 | _lqKdPxmxRepository = lqKdPxmxRepository; |
| 57 | 63 | _userManager = userManager; |
| 64 | + _weChatBotService = weChatBotService; | |
| 65 | + _stringGenerator = stringGenerator; | |
| 58 | 66 | } |
| 59 | 67 | |
| 60 | 68 | /// <summary> |
| ... | ... | @@ -203,6 +211,41 @@ namespace NCC.Extend.LqKdKdjlb |
| 203 | 211 | |
| 204 | 212 | //关闭事务 |
| 205 | 213 | _db.CommitTran(); |
| 214 | + | |
| 215 | + // 生成开单记录字符串并发送到企业微信 | |
| 216 | + try | |
| 217 | + { | |
| 218 | + var entityInfo = await GetInfo(newEntity.Id); | |
| 219 | + if (entityInfo != null) | |
| 220 | + { | |
| 221 | + var orderRecordString = _stringGenerator.GenerateOrderRecordString(entityInfo); | |
| 222 | + Console.WriteLine("开单记录字符串生成成功:"); | |
| 223 | + Console.WriteLine(orderRecordString); | |
| 224 | + | |
| 225 | + // 发送到企业微信群 | |
| 226 | + try | |
| 227 | + { | |
| 228 | + var sendResult = await _weChatBotService.SendOrderRecordMessage(orderRecordString); | |
| 229 | + if (sendResult) | |
| 230 | + { | |
| 231 | + Console.WriteLine("开单记录已成功发送到企业微信群"); | |
| 232 | + } | |
| 233 | + else | |
| 234 | + { | |
| 235 | + Console.WriteLine("开单记录发送到企业微信群失败"); | |
| 236 | + } | |
| 237 | + } | |
| 238 | + catch (Exception wechatEx) | |
| 239 | + { | |
| 240 | + Console.WriteLine($"发送企业微信消息异常: {wechatEx.Message}"); | |
| 241 | + } | |
| 242 | + } | |
| 243 | + } | |
| 244 | + catch (Exception ex) | |
| 245 | + { | |
| 246 | + // 字符串生成失败不影响主流程,只记录日志 | |
| 247 | + Console.WriteLine($"生成开单记录字符串失败: {ex.Message}"); | |
| 248 | + } | |
| 206 | 249 | } |
| 207 | 250 | catch (Exception) |
| 208 | 251 | { |
| ... | ... | @@ -473,5 +516,6 @@ namespace NCC.Extend.LqKdKdjlb |
| 473 | 516 | throw NCCException.Oh(ErrorCode.COM1002); |
| 474 | 517 | } |
| 475 | 518 | } |
| 519 | + | |
| 476 | 520 | } |
| 477 | 521 | } | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/Utils/LqKdKdjlbStringGenerator.cs
0 → 100644
| 1 | +using System; | |
| 2 | +using System.Collections.Generic; | |
| 3 | +using System.Linq; | |
| 4 | +using System.Text; | |
| 5 | +using NCC.Extend.Entitys.Dto.LqKdKdjlb; | |
| 6 | +using NCC.Extend.Entitys; | |
| 7 | +using NCC.System.Entitys.Permission; | |
| 8 | +using SqlSugar; | |
| 9 | + | |
| 10 | +namespace NCC.Extend.Utils | |
| 11 | +{ | |
| 12 | + /// <summary> | |
| 13 | + /// 开单记录字符串生成器 | |
| 14 | + /// </summary> | |
| 15 | + public class LqKdKdjlbStringGenerator | |
| 16 | + { | |
| 17 | + private readonly ISqlSugarClient _db; | |
| 18 | + | |
| 19 | + public LqKdKdjlbStringGenerator(ISqlSugarClient db) | |
| 20 | + { | |
| 21 | + _db = db; | |
| 22 | + } | |
| 23 | + | |
| 24 | + /// <summary> | |
| 25 | + /// 生成开单记录字符串 | |
| 26 | + /// </summary> | |
| 27 | + /// <param name="entity">开单记录实体</param> | |
| 28 | + /// <returns>格式化的字符串</returns> | |
| 29 | + public string GenerateOrderRecordString(LqKdKdjlbInfoOutput entity) | |
| 30 | + { | |
| 31 | + if (entity == null) | |
| 32 | + { | |
| 33 | + return string.Empty; | |
| 34 | + } | |
| 35 | + var sb = new StringBuilder(); | |
| 36 | + // 店名:龙城国际店 - 获取显示名称 | |
| 37 | + var storeName = GetStoreDisplayName(entity.djmd); | |
| 38 | + sb.AppendLine($"⏩店名:{storeName}"); | |
| 39 | + // 金三角:ko队 - 获取部门显示名称 | |
| 40 | + var departmentName = GetDepartmentDisplayName(entity.jsj); | |
| 41 | + sb.AppendLine($"⏩金三角:{departmentName}"); | |
| 42 | + // 顾客姓名:高华 - 通过客户ID查询客户名称 | |
| 43 | + var customerName = GetCustomerName(entity.kdhy); | |
| 44 | + sb.AppendLine($"⏩顾客姓名:{customerName}"); | |
| 45 | + // 健康师:王维 | |
| 46 | + var healthTeachers = GetHealthTeachers(entity.lqKdJksyjList); | |
| 47 | + sb.AppendLine($"⏩健康师:{healthTeachers}"); | |
| 48 | + | |
| 49 | + // 活动方案:532工程 | |
| 50 | + sb.AppendLine($"⏩活动方案:{entity.pxxx ?? "无"}"); | |
| 51 | + | |
| 52 | + // 跟单配合:王经理 竹主任 陈思思老师 | |
| 53 | + var techTeachers = GetTechTeachers(entity.lqKdKjbsyjList); | |
| 54 | + sb.AppendLine($"⏩跟单配合:{techTeachers}"); | |
| 55 | + | |
| 56 | + // 业绩:4800 | |
| 57 | + sb.AppendLine($"⏩业绩:{entity.zdyj}"); | |
| 58 | + | |
| 59 | + // 实付:4800 | |
| 60 | + sb.AppendLine($"⏩实付:{entity.sfyj}"); | |
| 61 | + | |
| 62 | + // 欠款: | |
| 63 | + sb.AppendLine($"⏩欠款: {entity.qk}"); | |
| 64 | + | |
| 65 | + // 抵扣: | |
| 66 | + sb.AppendLine($"⏩抵扣:{entity.ckmx ?? "无"}"); | |
| 67 | + | |
| 68 | + // 来源:售后 | |
| 69 | + sb.AppendLine($"⏩来源:{entity.khly ?? "无"}"); | |
| 70 | + | |
| 71 | + // 是否属于升单: | |
| 72 | + sb.AppendLine($"⏩是否属于升单:{entity.sfskdd ?? "无"}"); | |
| 73 | + | |
| 74 | + // 简介:高姐是我们的老客,今天邀约到犀浦店做532,维维全程陪同,陈思思老师在操作过程中给姐姐分享企业文化,找到顾客需求、邀请王经理 竹主任 给到顾客福利、顾客爽快成交,私密档案已了解到70%,感谢顾客的信任与支持 ,王经理 竹主任 陈老师辛苦了[玫瑰]维维真棒[强]龙城国际店加油!我们还在努力中!家人们给我们打气加油👏👏👏👏等待我们的捷报👍👍 | |
| 75 | + sb.AppendLine($"⏩简介:{entity.jj ?? "无"}"); | |
| 76 | + | |
| 77 | + return sb.ToString(); | |
| 78 | + } | |
| 79 | + | |
| 80 | + /// <summary> | |
| 81 | + /// 获取门店显示名称 | |
| 82 | + /// </summary> | |
| 83 | + /// <param name="storeCode">门店编码</param> | |
| 84 | + /// <returns>门店显示名称</returns> | |
| 85 | + private string GetStoreDisplayName(string storeCode) | |
| 86 | + { | |
| 87 | + if (string.IsNullOrEmpty(storeCode)) | |
| 88 | + { | |
| 89 | + return "无"; | |
| 90 | + } | |
| 91 | + | |
| 92 | + try | |
| 93 | + { | |
| 94 | + var store = _db.Queryable<LqMdxxEntity>() | |
| 95 | + .Where(x => x.Mdbm == storeCode) | |
| 96 | + .First(); | |
| 97 | + | |
| 98 | + return store?.Dm ?? storeCode; | |
| 99 | + } | |
| 100 | + catch | |
| 101 | + { | |
| 102 | + return storeCode; | |
| 103 | + } | |
| 104 | + } | |
| 105 | + | |
| 106 | + /// <summary> | |
| 107 | + /// 获取部门显示名称 | |
| 108 | + /// </summary> | |
| 109 | + /// <param name="departmentCode">部门编码</param> | |
| 110 | + /// <returns>部门显示名称</returns> | |
| 111 | + private string GetDepartmentDisplayName(string departmentCode) | |
| 112 | + { | |
| 113 | + if (string.IsNullOrEmpty(departmentCode)) | |
| 114 | + { | |
| 115 | + return "无"; | |
| 116 | + } | |
| 117 | + | |
| 118 | + try | |
| 119 | + { | |
| 120 | + var department = _db.Queryable<LqYcsdJsjEntity>() | |
| 121 | + .Where(x => x.Id == departmentCode) | |
| 122 | + .First(); | |
| 123 | + | |
| 124 | + return department?.Jsj ?? departmentCode; | |
| 125 | + } | |
| 126 | + catch | |
| 127 | + { | |
| 128 | + return departmentCode; | |
| 129 | + } | |
| 130 | + } | |
| 131 | + | |
| 132 | + /// <summary> | |
| 133 | + /// 获取健康师信息 | |
| 134 | + /// </summary> | |
| 135 | + /// <param name="healthTeacherList">健康师列表</param> | |
| 136 | + /// <returns>健康师姓名字符串</returns> | |
| 137 | + private string GetHealthTeachers(List<LqKdJksyjInfoOutput> healthTeacherList) | |
| 138 | + { | |
| 139 | + if (healthTeacherList == null || !healthTeacherList.Any()) | |
| 140 | + { | |
| 141 | + return "无"; | |
| 142 | + } | |
| 143 | + | |
| 144 | + var names = new List<string>(); | |
| 145 | + foreach (var teacher in healthTeacherList) | |
| 146 | + { | |
| 147 | + if (!string.IsNullOrEmpty(teacher.jks)) | |
| 148 | + { | |
| 149 | + // 通过账号查询用户真实姓名 | |
| 150 | + var realName = GetUserRealNameByAccount(teacher.jks); | |
| 151 | + if (!string.IsNullOrEmpty(realName)) | |
| 152 | + { | |
| 153 | + names.Add(realName); | |
| 154 | + } | |
| 155 | + } | |
| 156 | + else if (!string.IsNullOrEmpty(teacher.jksxm)) | |
| 157 | + { | |
| 158 | + // 如果没有账号,使用原有的姓名字段 | |
| 159 | + names.Add(teacher.jksxm); | |
| 160 | + } | |
| 161 | + } | |
| 162 | + | |
| 163 | + return names.Distinct().Any() ? string.Join(" ", names.Distinct()) : "无"; | |
| 164 | + } | |
| 165 | + | |
| 166 | + /// <summary> | |
| 167 | + /// 获取科技部老师信息 | |
| 168 | + /// </summary> | |
| 169 | + /// <param name="techTeacherList">科技部老师列表</param> | |
| 170 | + /// <returns>科技部老师姓名字符串</returns> | |
| 171 | + private string GetTechTeachers(List<LqKdKjbsyjInfoOutput> techTeacherList) | |
| 172 | + { | |
| 173 | + if (techTeacherList == null || !techTeacherList.Any()) | |
| 174 | + { | |
| 175 | + return "无"; | |
| 176 | + } | |
| 177 | + | |
| 178 | + var names = new List<string>(); | |
| 179 | + foreach (var teacher in techTeacherList) | |
| 180 | + { | |
| 181 | + if (!string.IsNullOrEmpty(teacher.kjbls)) | |
| 182 | + { | |
| 183 | + // 通过账号查询用户真实姓名 | |
| 184 | + var realName = GetUserRealNameByAccount(teacher.kjbls); | |
| 185 | + if (!string.IsNullOrEmpty(realName)) | |
| 186 | + { | |
| 187 | + names.Add(realName); | |
| 188 | + } | |
| 189 | + } | |
| 190 | + else if (!string.IsNullOrEmpty(teacher.kjblsxm)) | |
| 191 | + { | |
| 192 | + // 如果没有账号,使用原有的姓名字段 | |
| 193 | + names.Add(teacher.kjblsxm); | |
| 194 | + } | |
| 195 | + } | |
| 196 | + | |
| 197 | + return names.Distinct().Any() ? string.Join(" ", names.Distinct()) : "无"; | |
| 198 | + } | |
| 199 | + | |
| 200 | + /// <summary> | |
| 201 | + /// 获取客户名称 | |
| 202 | + /// </summary> | |
| 203 | + /// <param name="customerId">客户ID</param> | |
| 204 | + /// <returns>客户名称</returns> | |
| 205 | + private string GetCustomerName(string customerId) | |
| 206 | + { | |
| 207 | + if (string.IsNullOrEmpty(customerId)) | |
| 208 | + { | |
| 209 | + return "无"; | |
| 210 | + } | |
| 211 | + | |
| 212 | + try | |
| 213 | + { | |
| 214 | + var customer = _db.Queryable<LqKhxxEntity>() | |
| 215 | + .Where(x => x.Id == customerId) | |
| 216 | + .First(); | |
| 217 | + | |
| 218 | + return customer?.Khmc ?? "无"; | |
| 219 | + } | |
| 220 | + catch | |
| 221 | + { | |
| 222 | + return "无"; | |
| 223 | + } | |
| 224 | + } | |
| 225 | + | |
| 226 | + /// <summary> | |
| 227 | + /// 通过账号查询用户真实姓名 | |
| 228 | + /// </summary> | |
| 229 | + /// <param name="account">用户账号</param> | |
| 230 | + /// <returns>用户真实姓名</returns> | |
| 231 | + private string GetUserRealNameByAccount(string account) | |
| 232 | + { | |
| 233 | + if (string.IsNullOrEmpty(account)) | |
| 234 | + { | |
| 235 | + return string.Empty; | |
| 236 | + } | |
| 237 | + | |
| 238 | + try | |
| 239 | + { | |
| 240 | + var user = _db.Queryable<UserEntity>() | |
| 241 | + .Where(x => x.Account == account && x.DeleteMark == null) | |
| 242 | + .First(); | |
| 243 | + | |
| 244 | + return user?.RealName ?? string.Empty; | |
| 245 | + } | |
| 246 | + catch | |
| 247 | + { | |
| 248 | + return string.Empty; | |
| 249 | + } | |
| 250 | + } | |
| 251 | + | |
| 252 | + } | |
| 253 | +} | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/Utils/WeChatBotService.cs
0 → 100644
| 1 | +using System; | |
| 2 | +using System.Net.Http; | |
| 3 | +using System.Text; | |
| 4 | +using System.Threading.Tasks; | |
| 5 | +using Newtonsoft.Json; | |
| 6 | + | |
| 7 | +namespace NCC.Extend.Utils | |
| 8 | +{ | |
| 9 | + /// <summary> | |
| 10 | + /// 企业微信机器人服务 | |
| 11 | + /// </summary> | |
| 12 | + public class WeChatBotService | |
| 13 | + { | |
| 14 | + private readonly HttpClient _httpClient; | |
| 15 | + private const string BOT_API_URL = "http://wx.lvqianmeiye.com/api/Bot/send-text"; | |
| 16 | + private const string WEBHOOK_URL = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=6f8686ec-5011-4c1d-bae9-d82a2a2f4d83"; | |
| 17 | + | |
| 18 | + public WeChatBotService(HttpClient httpClient) | |
| 19 | + { | |
| 20 | + _httpClient = httpClient; | |
| 21 | + } | |
| 22 | + | |
| 23 | + /// <summary> | |
| 24 | + /// 发送文本消息到企业微信群 | |
| 25 | + /// </summary> | |
| 26 | + /// <param name="content">消息内容</param> | |
| 27 | + /// <returns>发送结果</returns> | |
| 28 | + public async Task<bool> SendTextMessage(string content) | |
| 29 | + { | |
| 30 | + try | |
| 31 | + { | |
| 32 | + var requestData = new | |
| 33 | + { | |
| 34 | + webhookUrl = WEBHOOK_URL, | |
| 35 | + content = content, | |
| 36 | + mentionedList = (string)null, | |
| 37 | + mentionedMobileList = (string)null | |
| 38 | + }; | |
| 39 | + | |
| 40 | + var json = JsonConvert.SerializeObject(requestData); | |
| 41 | + var httpContent = new StringContent(json, Encoding.UTF8, "application/json"); | |
| 42 | + | |
| 43 | + var response = await _httpClient.PostAsync(BOT_API_URL, httpContent); | |
| 44 | + | |
| 45 | + if (response.IsSuccessStatusCode) | |
| 46 | + { | |
| 47 | + var responseContent = await response.Content.ReadAsStringAsync(); | |
| 48 | + Console.WriteLine($"企业微信消息发送成功: {responseContent}"); | |
| 49 | + return true; | |
| 50 | + } | |
| 51 | + else | |
| 52 | + { | |
| 53 | + var errorContent = await response.Content.ReadAsStringAsync(); | |
| 54 | + Console.WriteLine($"企业微信消息发送失败: {response.StatusCode} - {errorContent}"); | |
| 55 | + return false; | |
| 56 | + } | |
| 57 | + } | |
| 58 | + catch (Exception ex) | |
| 59 | + { | |
| 60 | + Console.WriteLine($"企业微信消息发送异常: {ex.Message}"); | |
| 61 | + return false; | |
| 62 | + } | |
| 63 | + } | |
| 64 | + | |
| 65 | + /// <summary> | |
| 66 | + /// 发送开单记录消息 | |
| 67 | + /// </summary> | |
| 68 | + /// <param name="orderRecordString">开单记录字符串</param> | |
| 69 | + /// <returns>发送结果</returns> | |
| 70 | + public async Task<bool> SendOrderRecordMessage(string orderRecordString) | |
| 71 | + { | |
| 72 | + if (string.IsNullOrEmpty(orderRecordString)) | |
| 73 | + { | |
| 74 | + Console.WriteLine("开单记录字符串为空,跳过发送"); | |
| 75 | + return false; | |
| 76 | + } | |
| 77 | + | |
| 78 | + // 添加标题和格式化 | |
| 79 | + var messageContent = $"🎉 新开单记录\n\n{orderRecordString}"; | |
| 80 | + | |
| 81 | + return await SendTextMessage(messageContent); | |
| 82 | + } | |
| 83 | + } | |
| 84 | +} | ... | ... |
netcore/src/Modularity/Extend/NCC.Extend/Utils/WeChatBotTestController.cs
0 → 100644
| 1 | +using Microsoft.AspNetCore.Mvc; | |
| 2 | +using NCC.DynamicApiController; | |
| 3 | +using NCC.Dependency; | |
| 4 | +using System.Threading.Tasks; | |
| 5 | +using System.Collections.Generic; | |
| 6 | +using NCC.Extend.Entitys.Dto.LqKdKdjlb; | |
| 7 | + | |
| 8 | +namespace NCC.Extend.Utils | |
| 9 | +{ | |
| 10 | + /// <summary> | |
| 11 | + /// 企业微信机器人测试控制器 | |
| 12 | + /// </summary> | |
| 13 | + [ApiDescriptionSettings(Tag = "Extend", Name = "WeChatBotTest", Order = 200)] | |
| 14 | + [Route("api/Extend/[controller]")] | |
| 15 | + public class WeChatBotTestController : IDynamicApiController, ITransient | |
| 16 | + { | |
| 17 | + private readonly WeChatBotService _weChatBotService; | |
| 18 | + private readonly LqKdKdjlbStringGenerator _stringGenerator; | |
| 19 | + | |
| 20 | + public WeChatBotTestController(WeChatBotService weChatBotService, LqKdKdjlbStringGenerator stringGenerator) | |
| 21 | + { | |
| 22 | + _weChatBotService = weChatBotService; | |
| 23 | + _stringGenerator = stringGenerator; | |
| 24 | + } | |
| 25 | + | |
| 26 | + /// <summary> | |
| 27 | + /// 测试发送文本消息 | |
| 28 | + /// </summary> | |
| 29 | + /// <param name="content">消息内容</param> | |
| 30 | + /// <returns>发送结果</returns> | |
| 31 | + [HttpPost("SendText")] | |
| 32 | + public async Task<bool> SendTextMessage(string content = "这是一条测试消息") | |
| 33 | + { | |
| 34 | + return await _weChatBotService.SendTextMessage(content); | |
| 35 | + } | |
| 36 | + | |
| 37 | + /// <summary> | |
| 38 | + /// 测试发送开单记录消息 | |
| 39 | + /// </summary> | |
| 40 | + /// <returns>发送结果</returns> | |
| 41 | + [HttpPost("SendOrderRecord")] | |
| 42 | + public async Task<bool> SendOrderRecordMessage() | |
| 43 | + { | |
| 44 | + var testOrderRecord = @"---------------- | |
| 45 | +⏩店名:测试门店 | |
| 46 | +⏩金三角:测试团队 | |
| 47 | +⏩顾客姓名:测试客户 | |
| 48 | +⏩健康师:测试健康师 | |
| 49 | +⏩活动方案:测试方案 | |
| 50 | +⏩跟单配合:测试配合 | |
| 51 | +⏩业绩:1000 | |
| 52 | +⏩实付:1000 | |
| 53 | +⏩欠款: 0 | |
| 54 | +⏩抵扣:无 | |
| 55 | +⏩来源:测试来源 | |
| 56 | +⏩是否属于升单:无 | |
| 57 | +⏩简介:这是一条测试开单记录"; | |
| 58 | + | |
| 59 | + return await _weChatBotService.SendOrderRecordMessage(testOrderRecord); | |
| 60 | + } | |
| 61 | + | |
| 62 | + /// <summary> | |
| 63 | + /// 测试字符串生成器(验证显示名称功能) | |
| 64 | + /// </summary> | |
| 65 | + /// <returns>生成的字符串</returns> | |
| 66 | + [HttpPost("TestStringGenerator")] | |
| 67 | + public string TestStringGenerator() | |
| 68 | + { | |
| 69 | + // 创建测试数据 | |
| 70 | + var testEntity = new LqKdKdjlbInfoOutput | |
| 71 | + { | |
| 72 | + id = "test001", | |
| 73 | + djmd = "TEST001", // 门店编码 | |
| 74 | + jsj = "DEPT001", // 部门编码 | |
| 75 | + kdhy = "CUSTOMER001", // 客户ID | |
| 76 | + kdhyc = "测试客户", | |
| 77 | + pxxx = "测试方案", | |
| 78 | + zdyj = 1000, | |
| 79 | + sfyj = 1000, | |
| 80 | + qk = 0, | |
| 81 | + ckmx = "无", | |
| 82 | + khly = "测试来源", | |
| 83 | + sfskdd = "无", | |
| 84 | + jj = "这是一条测试开单记录", | |
| 85 | + lqKdJksyjList = new List<LqKdJksyjInfoOutput> | |
| 86 | + { | |
| 87 | + new LqKdJksyjInfoOutput { jks = "test_health_1", jksxm = "测试健康师1" }, | |
| 88 | + new LqKdJksyjInfoOutput { jks = "test_health_2", jksxm = "测试健康师2" } | |
| 89 | + }, | |
| 90 | + lqKdKjbsyjList = new List<LqKdKjbsyjInfoOutput> | |
| 91 | + { | |
| 92 | + new LqKdKjbsyjInfoOutput { kjbls = "test_tech_1", kjblsxm = "测试科技部1" }, | |
| 93 | + new LqKdKjbsyjInfoOutput { kjbls = "test_tech_2", kjblsxm = "测试科技部2" } | |
| 94 | + } | |
| 95 | + }; | |
| 96 | + | |
| 97 | + return _stringGenerator.GenerateOrderRecordString(testEntity); | |
| 98 | + } | |
| 99 | + } | |
| 100 | +} | ... | ... |
参考资料/.DS_Store
0 → 100644
No preview for this file type
参考资料/工资核算 -7月/.DS_Store
0 → 100644
No preview for this file type
参考资料/工资核算 -7月/B-b-③健康师底薪.xlsx
0 → 100644
No preview for this file type
参考资料/工资核算 -7月/B-b-④健康师提成-金三角顾问.xlsx
0 → 100644
No preview for this file type
参考资料/工资核算 -7月/B-b-⑤其他岗位工资.xlsx
0 → 100644
No preview for this file type
参考资料/工资核算 -7月/B-b-⑤当月数据及门店毛利.xlsx
0 → 100644
No preview for this file type
参考资料/工资核算 -7月/①B-a-26考勤汇总表.xlsx
0 → 100644
No preview for this file type
参考资料/工资核算 -7月/②B-a-17每日早报.xlsx
0 → 100644
No preview for this file type
参考资料/工资核算 -7月/③B-a-③呈现-消耗明细表.xlsx
0 → 100644
No preview for this file type
参考资料/工资核算 -7月/④B-a-25社保统计表.xlsx
0 → 100644
No preview for this file type
参考资料/工资核算 -7月/⑤B-a-12奖励统计表.xlsx
0 → 100644
No preview for this file type
参考资料/工资核算 -7月/外部表单/2025年7月工资(2)(3)(2) 的副本-郭主管8-14.xlsx
0 → 100644
No preview for this file type
参考资料/工资核算 -7月/外部表单/2025年7月工资表 的副本-张总裁.xlsx
0 → 100644
No preview for this file type
参考资料/工资核算 -7月/外部表单/7月-考勤.xlsx
0 → 100644
No preview for this file type
参考资料/工资核算 -7月/工资(全字段).xlsx
0 → 100644
No preview for this file type
参考资料/工资核算 -7月/流程-工资核算的相关表格及sop流程.xlsx
0 → 100644
No preview for this file type
参考资料/整体-8.15/.DS_Store
0 → 100644
No preview for this file type
参考资料/整体-8.15/2025嘉宾挑战V4.xlsx
0 → 100644
No preview for this file type
参考资料/整体-8.15/A-a前置信息/A-a-①前置信息-门店资料(确定版)-8.5更新.xlsx
0 → 100644
No preview for this file type
参考资料/整体-8.15/A-a前置信息/A-a-②前置信息-人员资料-8.10更新.xlsx
0 → 100644
No preview for this file type
参考资料/整体-8.15/A-a前置信息/A-a-③前置信息-项目资料(7-24更新).xlsx
0 → 100644
No preview for this file type
参考资料/整体-8.15/A-a前置信息/A-a-④前置信息-客户资料-8.5更新.xlsx
0 → 100644
No preview for this file type
参考资料/整体-8.15/A-a前置信息/A-a-⑤前置信息-历史数据2024.xlsx
0 → 100644
No preview for this file type
参考资料/整体-8.15/A-a前置信息/A-a-⑥前置信息-信息字典-8.5更新.xlsx
0 → 100644
No preview for this file type
参考资料/整体-8.15/A-a前置信息/A-a-⑦前置信息-营销活动方案.xlsx
0 → 100644
No preview for this file type
参考资料/整体-8.15/A-a前置信息/A-a-⑧前置信息-产品资料-8.5更新.xlsx
0 → 100644
No preview for this file type
参考资料/整体-8.15/A-b每月月初设定/①门店目标设定.xlsx
0 → 100644
No preview for this file type
参考资料/整体-8.15/A-b每月月初设定/②金三角设定.xlsx
0 → 100644
No preview for this file type
参考资料/整体-8.15/A-b每月月初设定/④社保增减-8.5新增.xlsx
0 → 100644
No preview for this file type
参考资料/整体-8.15/A-b每月月初设定/⑤门店类别及核算信息.xlsx
0 → 100644
No preview for this file type
参考资料/整体-8.15/A-b每月月初设定/⑥工资核算浮动条件设置.xlsx
0 → 100644
No preview for this file type
参考资料/整体-8.15/A-b每月月初设定/⑦总经理经理核算条件.xlsx
0 → 100644
No preview for this file type
参考资料/整体-8.15/A-b每月月初设定/⑧毛巾品类及单价设定.xlsx
0 → 100644
No preview for this file type
参考资料/整体-8.15/A-c日常操作/.DS_Store
0 → 100644
No preview for this file type
参考资料/整体-8.15/A-c日常操作/A-c-10社保变动-8.5新增.xlsx
0 → 100644
No preview for this file type
参考资料/整体-8.15/A-c日常操作/A-c-①请假.xlsx
0 → 100644
No preview for this file type
参考资料/整体-8.15/A-c日常操作/A-c-②人员调动.xlsx
0 → 100644
No preview for this file type
参考资料/整体-8.15/A-c日常操作/A-c-③宿舍.xlsx
0 → 100644
No preview for this file type
参考资料/整体-8.15/A-c日常操作/A-c-⑤业务操作-6个版块/.DS_Store
0 → 100644
No preview for this file type
参考资料/整体-8.15/A-c日常操作/A-c-⑤业务操作-6个版块/副本播报-字段(1).xlsx
0 → 100644
No preview for this file type
参考资料/整体-8.15/A-c日常操作/A-c-⑤业务操作-6个版块/拓客字段-报表-奖励-科目/拓客汇总表【带奖励】.xlsx
0 → 100644
No preview for this file type
参考资料/整体-8.15/A-c日常操作/A-c-⑤业务操作-6个版块/拓客字段-报表-奖励-科目/插件②-拓客.xlsx
0 → 100644
No preview for this file type
参考资料/整体-8.15/A-c日常操作/A-c-⑤业务操作-6个版块/插件-预约.xlsx
0 → 100644
No preview for this file type
参考资料/整体-8.15/A-c日常操作/A-c-⑤业务操作-6个版块/消耗.xlsx
0 → 100644
No preview for this file type
参考资料/整体-8.15/A-c日常操作/A-c-⑤业务操作-6个版块/退卡.xlsx
0 → 100644
No preview for this file type
参考资料/整体-8.15/A-c日常操作/A-c-⑤业务操作-6个版块/邀约字段-报表/插件③-邀约.xlsx
0 → 100644
No preview for this file type
参考资料/整体-8.15/A-c日常操作/A-c-⑥报销及明细上传.xlsx
0 → 100644
No preview for this file type
参考资料/整体-8.15/A-c日常操作/A-c-⑧毛巾的收发登记.xlsx
0 → 100644
No preview for this file type
参考资料/整体-8.15/A-c日常操作/A-c-⑨每日工作-科技部.xlsx
0 → 100644
No preview for this file type
参考资料/整体-8.15/B-a-报表字段/B-a-⑧每日早报.xlsx
0 → 100644
No preview for this file type
参考资料/整体-8.15/B-a-报表字段/B-a-(①-16)报表字段及股份字段.xlsx
0 → 100644
No preview for this file type
参考资料/整体-8.15/B-b-②股份核算套表-8.8.xlsx
0 → 100644
No preview for this file type
参考资料/整体-8.15/增加/A-a-①-增加.xlsx
0 → 100644
No preview for this file type
参考资料/整体-8.15/增加/A-a-②-增加.xlsx
0 → 100644
No preview for this file type
参考资料/整体-8.15/增加/A-b-②-增加.xlsx
0 → 100644
No preview for this file type
参考资料/整体-8.15/增加/A-b-⑨-增加.xlsx
0 → 100644
No preview for this file type
参考资料/整体-8.15/整体-(8-14更新).xlsx
0 → 100644
No preview for this file type