基于C语言的小型超市库存与销售管理系统

Expiredlove

发布日期: 2018-11-04 14:56:40 浏览量: 1378
评分:
star star star star star star star star star star
*转载请注明来自write-bug.com

1 需求分析

1.1 登陆

管理员和售货员可通过各自的账号、密码分别进入管理员和售货员的子系统。对于输入不在系统所存储的账号或输入的账号密码不匹配时,要求用户重新输入。

1.2 用户管理

管理员用户可浏览系统内所有的用户的账号、密码、权限类别,可添加用户,可删除用户。

1.3 库存管理

管理员可手动添加商品,也可从文件中批量导入商品,可查看库存内的全部商品信息,对于库存内商品数为0的商品可进行批量清理。

1.4 查询商品

管理员和售货员可通过商品名称、商品生产商、名称和生产商的方式查询商品信息。管理员可获取全部商品信息(商品ID、商品名称、进价、售价、生产厂商、余量),售货员可获取出进价以外的商品信息。查询可支持模糊查找、仅输入前缀。

1.5 销售商品

管理员和售货员可对库存内商品进行销售,对销售请求进行检查,销售后对库存相应商品的余量进行更新,同时记录销售的商品信息、销售时间,更新销售记录数据文件。

1.6 销售统计

管理员可浏览某天的或日期区间内的所有销售记录,可对指定日期区间内的销售记录进行综合统计,统计每种商品的销量、收入,统计总收入,可通过销量、销售额筛选统计结果。

2 概要设计

2.1 数据结构

单个商品数据用Goods结构体存储,多个商品用链表存储。

  1. typedef struct
  2. {
  3. int id;
  4. char name[MAXGOODSNAME];
  5. double buying_price;
  6. double selling_price;
  7. char manufacturer[MAXMANUFACTURERNAME];
  8. int quantity;
  9. } Goods;
  10. typedef struct GoodsListNode *GoodsList;
  11. struct GoodsListNode
  12. {
  13. Goods goods;
  14. GoodsList next;
  15. };

单个销售数据用SoldGoodsRecord结构体存储,多个销售数据用链表存储。

  1. typedef struct
  2. {
  3. int id;
  4. char name[MAXGOODSNAME];
  5. double buying_price;
  6. double selling_price;
  7. int sold_quantity;
  8. SoldDate date;
  9. }SoldGoodsRecord;
  10. typedef struct RecordsListNode *RecordsList;
  11. struct RecordsListNode
  12. {
  13. SoldGoodsRecord record;
  14. RecordsList next;
  15. };

2.2 模块划分

2.2.1 管理商品模块 manage_goods.c

对商品进行操作的函数集。

  1. // 初始化商品链表
  2. GoodsList InitGoodsList();
  3. // 销毁商品链表
  4. void DeleteGoodsList(GoodsList head);
  5. // 向链表添加商品
  6. int AddGoodsToList(GoodsList head, Goods goods);
  7. // 遍历商品链表
  8. void TraverseGoodsList(GoodsList head, void(*Fun)(Goods *));
  9. // 显示商品信息
  10. void DisplayGoodsInfo(Goods *goods);
  11. // 显示商品基本信息
  12. void DisplayBasicGoodsInfo(Goods *goods);
  13. // 添加某商品数量:
  14. int IncreaseGoodsQuantity(GoodsList head, int id, int quantity);
  15. // 减少某商品数量:
  16. int ReduceGoodsQuantity(GoodsList head, int id, int quantity);
  17. // 从文件中导入商品
  18. void ImportGoodsFromFile(GoodsList head, FILE *fp);
  19. // 导出商品至文件
  20. void ExportGoodsToFile(GoodsList head, FILE *fp);
  21. // 清空数量为0商品
  22. void RemoveZeroQuantityGoods(GoodsList head);
  23. // 根据ID查找商品
  24. GoodsList FindGoodsByID(GoodsList head, int id);
  25. // 打开商品文件
  26. FILE* OpenGoodsFile(char *mod);

2.2.2 管理销售记录模块 manage_records.c

  1. // 初始化销售记录链表
  2. RecordsList InitRecordsList();
  3. // 销毁销售记录链表
  4. void DeleteRecordsList(RecordsList head);
  5. // 遍历销售记录链表
  6. void TraverseRecordsList(RecordsList head, void(*Fun)(SoldGoodsRecord *));
  7. // 显示一条销售记录信息
  8. void DisplayARecordInfo(SoldGoodsRecord *record);
  9. // 向文件追加一条销售记录
  10. void AppendARecordToFile(SoldGoodsRecord record, FILE *fp);
  11. // 向链表条件一条销售记录
  12. void AddRecordToList(RecordsList head, SoldGoodsRecord record);
  13. // 从文件中导入销售记录数据至链表
  14. void ImportRecordsFromFile(RecordsList head, FILE *fp);
  15. // 获取当前销售时间
  16. SoldDate GetNowDate();
  17. // 打开销售记录文件
  18. FILE* OpenRecordsFile(char *mod);

2.2.3 查询模块 query.c

  1. // 按名字在链表中查找商品
  2. GoodsList QueryGoodsByName(GoodsList head, char *name);
  3. // 按生产商在链表中查找商品
  4. GoodsList QueryGoodsByManufacturer(GoodsList head, char *manufacturer)
  5. // 按名字和生产商在链表中查找商品
  6. GoodsList QueryGoodsByNameAndManufacturer(GoodsList head, char *goods_name, char *manufacturer);
  7. // 按日期区间查找销售记录
  8. void QuerySoldRecordsByDate(RecordsList head, SoldDate start, SoldDate end);

2.2.4 统计模块 statistics.c

  1. // 按日期区间对销售商品进行统计,通过销量、销售额筛选统计结果
  2. void SoldStatisticsByDate(RecordsList head, SoldDate start, SoldDate end, int min_sold_cnt, int min_earnings);

2.2.5 售货员模块 salesman.c

  1. // 判断是否为销售员用户
  2. int IsSalesmanAccount(char *account, char *password);
  3. // 显示待售商品
  4. void DisplaySoldGoods(GoodsList head);
  5. // 销售商品
  6. void SoldGoods(GoodsList head);
  7. // 销售员初始化菜单
  8. void SalesmanInitMenu();
  9. // 查询商品菜单
  10. void SalesmanLookUpGoods(GoodsList head);
  11. // 按名称查询商品
  12. void SalesmanLookUpGoodsByName(GoodsList head);
  13. // 按生产商查询商品
  14. void SalesmanLookUpGoodsByManufacturer(GoodsList head);
  15. // 按名称和生产商查询商品
  16. void SalesmanLookUpGoodsByNameAndManufacturer(GoodsList head);

2.2.6管理员模块 admin_user.c

  1. // 判断是否为管理员用户
  2. int IsAdminAccount(char *account, char *password);
  3. // 添加用户
  4. int AddAccount(UserAccount* user);
  5. // 删除用户
  6. int DeleteAccount(char *account);
  7. // 显示所有用户信息
  8. int DisplayAccountInfo();
  9. // 管理员初始菜单
  10. void AdminInitMenu();
  11. // 库存管理菜单
  12. void StockManagement();
  13. // 添加商品
  14. void AddGoodsToStock(GoodsList head);
  15. // 商品批量入库
  16. void BatchedStock(GoodsList head);
  17. // 查看库存商品
  18. void LookOverStock(GoodsList head);
  19. // 查询商品菜单
  20. void LookUpGoods(GoodsList head);
  21. // 按名称查询商品
  22. void LookUpGoodsByName(GoodsList head);
  23. // 按生产商查询商品
  24. void LookUpGoodsByManufacturer(GoodsList head);
  25. // 按名称和生产商查询商品
  26. void LookUpGoodsByNameAndManufacturer(GoodsList head);
  27. // 清理库存
  28. void ClearStock(GoodsList head);
  29. // 用户管理菜单
  30. void UserManagement();
  31. // 查看用户
  32. void LookOverUser();
  33. // 添加用户
  34. void AddUser();
  35. // 删除用户
  36. void DeleteUser();
  37. // 销售商品
  38. void SoldManagement();
  39. // 销售统计菜单
  40. void SoldStatistics();
  41. // 浏览单天销售记录
  42. void SingleDaySoldRecords(RecordsList head);
  43. // 浏览多天销售记录
  44. void DaysSoldRecords(RecordsList head);
  45. // 按日期区间浏览销售统计
  46. void DaysSoldStatistics(RecordsList head);

2.2.7 外部Hash模块 uthash.c

开源Hash模块,实现商品按id哈希处理,进行销售统计。

2.3 程序总体框架

3 详细设计

部分重要底层函数:

3.1 管理商品模块 manage_goods.c

  1. //初始化商品链表 创建带头结点的链表
  2. GoodsList InitGoodsList()
  3. {
  4. GoodsList head = (GoodsList)malloc(sizeof(struct GoodsListNode));
  5. head->next = NULL;
  6. return head;
  7. }
  8. //销毁链表
  9. void DeleteGoodsList(GoodsList head)
  10. {
  11. GoodsList next;
  12. while (head) {
  13. next = head->next;
  14. free(head);
  15. head = next;
  16. }
  17. }
  18. //在商品链表中添加一条商品信息,原有此商品数量合并,返回1
  19. //原没有,在链表末尾添加,返回0
  20. int AddGoodsToList(GoodsList head, Goods goods)
  21. {
  22. int id = goods.id;
  23. GoodsList p = head->next;
  24. while (p) {
  25. if (p->goods.id == id) {
  26. p->goods.quantity += goods.quantity;
  27. return 1;
  28. }
  29. p = p->next;
  30. }
  31. GoodsList newNode = (GoodsList)malloc(sizeof(struct GoodsListNode));
  32. newNode->goods = goods;
  33. newNode->next = head->next;
  34. head->next = newNode;
  35. return 0;
  36. }
  37. //从文件中导入商品数据
  38. void ImportGoodsFromFile(GoodsList head, FILE *fp)
  39. {
  40. Goods goods;
  41. while (!feof(fp))
  42. {
  43. fscanf(fp,"%d%s%lf%lf%s%d\n",&goods.id,goods.name,&goods.buying_price,&goods.selling_price,goods.manufacturer, &goods.quantity);
  44. AddGoodsToList(head, goods);
  45. }
  46. fclose(fp);
  47. }

3.2 管理销售记录模块 manage_records.c

  1. const char GOODS_SALES_RECORD_PATH[50] = "Data\\sold_goods_list.txt";
  2. //初始化销售记录裂变,创建带头结点的链表
  3. RecordsList InitRecordsList()
  4. {
  5. RecordsList head = (RecordsList)malloc(sizeof(struct RecordsListNode));
  6. head->next = NULL;
  7. return head;
  8. }
  9. //销毁销售记录链表
  10. void DeleteRecordsList(RecordsList head)
  11. {
  12. GoodsList next;
  13. while (head) {
  14. next = head->next;
  15. free(head);
  16. head = next;
  17. }
  18. }
  19. //在销售记录链表尾部增加一条销售记录信息
  20. void AddRecordToList(RecordsList head, SoldGoodsRecord record)
  21. {
  22. RecordsList newNode =
  23. (RecordsList)malloc(sizeof(struct RecordsListNode));
  24. newNode->record = record;
  25. newNode->next = NULL;
  26. RecordsList p = head;
  27. while (p->next) p = p->next;
  28. p->next = newNode;
  29. }
  30. //向文件中新增加一条销售记录数据
  31. void AppendARecordToFile(SoldGoodsRecord record, FILE *fp)
  32. {
  33. fprintf(fp, "%d %s %.2f %.2f %d %d-%d-%d-%d:%d:%d\n", record.id, record.name,record.buying_price,record.selling_price,record.sold_quantity,record.date.year,record.date.month,record.date.day,record.date.hour,record.date.min,record.date.second);
  34. fclose(fp);
  35. }
  36. //从文件中导出销售记录数据
  37. void ImportRecordsFromFile(RecordsList head, FILE *fp)
  38. {
  39. SoldGoodsRecord record;
  40. while (!feof(fp))
  41. {
  42. fscanf(fp, "%d %s %lf %lf %d %d-%d-%d-%d:%d:%d\n",
  43. &record.id, record.name,&record.buying_price,
  44. &record.selling_price,&record.sold_quantity,&record.date.year,
  45. &record.date.month,&record.date.day,&record.date.hour,
  46. &record.date.min,&record.date.second);
  47. AddRecordToList(head, record);
  48. }
  49. fclose(fp);
  50. }

3.3 查询模块 query.c

  1. //按照商品名和生产厂商前缀查询商品,输出商品基本信息
  2. GoodsList QueryGoodsByNameAndManufacturer(GoodsList head, char *goods_name, char *manufacturer)
  3. {
  4. if (head->next == NULL)
  5. return NULL;
  6. GoodsList queried_goods = InitGoodsList();
  7. GoodsList p = head->next;
  8. while (p) {
  9. char goods_name_prefix[MAXGOODSNAME] = { 0 };
  10. char manufacturer_prefix[MAXMANUFACTURERNAME] = { 0 };
  11. strncpy(goods_name_prefix, p->goods.name, strlen(goods_name));
  12. strncpy(manufacturer_prefix,p->goods.manufacturer, strlen(manufacturer));
  13. if(strcmp(goods_name_prefix,goods_name) == 0 && strcmp(manufacturer_prefix, manufacturer) == 0) {
  14. GoodsList newNode = (GoodsList)malloc(sizeof(struct GoodsListNode));
  15. newNode->goods = p->goods;
  16. newNode->next = queried_goods->next;
  17. queried_goods->next = newNode;
  18. }
  19. p = p->next;
  20. }
  21. return queried_goods;
  22. }

3.4 统计模块 statistics.c

  1. void SoldStatisticsByDate(RecordsList head, SoldDate start, SoldDate end, int min_sold_cnt, int min_earnings)
  2. {
  3. CountStatistics *s, *goods = NULL;
  4. RecordsList p = head->next;
  5. while (p) {
  6. if (CompareDate(p->record.date, start) >= 0 && CompareDate(p->record.date, end) <= 0) {
  7. HASH_FIND_INT(goods, &p->record.id, s);
  8. if (s) {
  9. s->cnt += p->record.sold_quantity;
  10. s->earnings += (p->record.selling_price p->record.buying_price) * p->record.sold_quantity;
  11. }
  12. else {
  13. s = (CountStatistics*)malloc(sizeof(CountStatistics));
  14. s->id = p->record.id;
  15. strcpy(s->name, p->record.name);
  16. s->cnt = p->record.sold_quantity;
  17. s->earnings = (p->record.selling_price- p->record.buying_price) * p->record.sold_quantity;
  18. HASH_ADD_INT(goods, id, s);
  19. }
  20. }
  21. p = p->next;
  22. }
  23. double total_earnings = 0;
  24. int total_goods_cnt = 0;
  25. printf("----------------------------------------\n");
  26. printf("%-5s %-12s %-6s %-6s\n", "ID", "名称", "销量", "收入");
  27. for (s = goods; s != NULL; s = (CountStatistics*)(s->hh.next)) {
  28. if (s->cnt >= min_sold_cnt && s->earnings >= min_earnings) {
  29. total_earnings += s->earnings;
  30. total_goods_cnt++;
  31. printf("%-5d %-12s %-6d %-6.2f\n", s->id, s->name, s->cnt, s->earnings);
  32. }
  33. }
  34. printf("\n总商品数:%d 总收入:%.2f元\n", total_goods_cnt, total_earnings);
  35. printf("----------------------------------------\n");
  36. }

3.5 售货员模块 salesman.c

  1. //购买商品,并更新商品,销售记录
  2. void SoldGoods(GoodsList head)
  3. {
  4. int id, cnt;
  5. GoodsList found_goods;
  6. while (1) {
  7. system("cls");
  8. DisplaySoldGoods(head);
  9. printf("\n输入待销售的商品ID (输入-1退出)\n>");
  10. scanf("%d", &id);
  11. if (id < 0) break;
  12. found_goods = FindGoodsByID(head, id);
  13. if (found_goods) {
  14. DisplayBasicGoodsInfo(&found_goods->goods);
  15. printf("输入销售数量\n>");
  16. scanf("%d", &cnt);
  17. if (cnt <= 0) {
  18. printf("商品数量有误\n");
  19. system("pause");
  20. continue;
  21. }
  22. else {
  23. FILE *goods_fp = OpenGoodsFile("w");
  24. FILE *records_fp = OpenRecordsFile("a");
  25. if (goods_fp && records_fp) {
  26. if (!ReduceGoodsQuantity(head, id, cnt)) {
  27. printf("该商品库存不足\n");
  28. system("pause");;
  29. continue;
  30. }
  31. ExportGoodsToFile(head, goods_fp);
  32. SoldGoodsRecord record;
  33. record.id = found_goods->goods.id;
  34. strcpy(record.name, found_goods->goods.name);
  35. record.selling_price = found_goods->goods.selling_price;
  36. record.sold_quantity = cnt;
  37. record.buying_price = found_goods->goods.buying_price;
  38. record.date = GetNowDate();
  39. AppendARecordToFile(record, records_fp);
  40. printf("销售成功\n");
  41. system("pause");
  42. continue;
  43. }
  44. else {
  45. if (goods_fp) fclose(goods_fp);
  46. if (records_fp) fclose(records_fp);
  47. printf("连接系统数据失败\n");
  48. system("pause");
  49. break;
  50. }
  51. }
  52. }
  53. else {
  54. printf("无此商品\n");
  55. system("pause");
  56. continue;
  57. }
  58. }
  59. }

3.6 管理员模块 admin_user.c

  1. //判断是否为管理员账户,无法打开文件返回-1,是返回1,否返回0
  2. int IsAdminAccount(char *account, char *password)
  3. {
  4. FILE *fp = fopen("Data\\user.dat", "rb");
  5. if (fp == NULL)
  6. return -1;
  7. fseek(fp, 0, SEEK_END);
  8. int size = ftell(fp)/sizeof(UserAccount);
  9. fseek(fp, 0, SEEK_SET);
  10. UserAccount* user = (UserAccount*)malloc(size * sizeof(UserAccount));
  11. for (int i = 0; i < size; i++) {
  12. fread(user + i, sizeof(UserAccount), 1, fp);
  13. if (strcmp(account, user[i].account) == 0 &&
  14. strcmp(password, user[i].password) == 0 &&
  15. user[i].permission_level == 1)
  16. return 1;
  17. }
  18. free(user);
  19. fclose(fp);
  20. return 0;
  21. }
  22. //增加账户信息,无法打开返回-1,成功返回1,账户已存在返回0(更改密码)
  23. int AddAccount(UserAccount *newInfo)
  24. {
  25. FILE *fp = fopen("Data\\user.dat", "rb+");
  26. if (fp == NULL)
  27. return -1;
  28. fseek(fp, 0, SEEK_END);
  29. int size = ftell(fp) / sizeof(UserAccount);
  30. fseek(fp, 0, SEEK_SET);
  31. int index = -1;
  32. UserAccount *user = (UserAccount*)malloc((size + 1)
  33. * sizeof(UserAccount));
  34. for (int i = 0; i < size; i++) {
  35. fread(user + i, sizeof(UserAccount), 1, fp);
  36. if (strcmp(user[i].account, newInfo->account) == 0) {
  37. return 0;
  38. }
  39. }
  40. user[size] = *newInfo;
  41. fseek(fp, 0, SEEK_SET);
  42. for (int i = 0; i <= size; i++) {
  43. fwrite(user + i, sizeof(UserAccount), 1, fp);
  44. }
  45. free(user);
  46. fclose(fp);
  47. return 1;
  48. }

4 调试分析

  • 问题1:链表操作易出错

    • 解决:为链表增加空的头结点,便于操作。销毁函数用于销毁链表,避免内存泄漏。对于同一级均要操作的链表的,在该级初始化链表,在返回上级时销毁
  • 问题2:需要读取多个文件

    • 解决:将文件读取封装为函数调用,对于售货功能,需要同时更新商品文件和销售记录文件,两个文件需要同时可打开时才可写入,采用类似原子操作思想
  • 问题3:交互逻辑使用不便。

    • 解决:改进交互逻辑,提供跳转功能、选择错误处理
  • 问题4:销售记录是分散记录,难于以商品为单位整合统计

    • 解决:采用开源的Hash库,通过Hash算法以商品id为key进行整合,算出该id商品的总销量和总收入
  • 问题5:指定日期区间查询或统计时不便

    • 解决:构造日期结构体,设置日期比较规则

程序改进

  • 改进操作逻辑,使得操作更加方便

  • 增强大量数据时的处理能力

  • 采用数据库管理信息

  • 考虑多用户并发问题

  • 增加程序鲁棒性

  • 改进交互

5 测试结果

6 参考文献

[1] 甘勇,李晔,卢冰.C语言程序设计[M].北京:中国铁道出版社,2014

[2] 谭浩强. C程序设计[M]. 北京:高等教育出版社,2010

[3] 苏小红,王宇颖,孙志岗. C语言程序设计[M]. 北京:高等教育出版社,2011

[4] 王新,孙雷. c语言课程设计[M]. 北京:高等教育出版社,2009

上传的附件 cloud_download 基于C语言的小型超市库存与销售管理系统.zip ( 605.53kb, 300次下载 )
error_outline 下载需要5点积分

发送私信

你最美丽的时光陪我度过那些年

5
文章数
5
评论数
最近文章
eject